[[ line['blocked'] and 'X' or '' ]]
@@ -241,7 +236,7 @@
[[ formatLang((reduce(lambda x, y: x + ((y['account_id']['type'] == 'receivable' and y['credit'] or 0) or (y['account_id']['type'] == 'payable' and y['debit'] * -1 or 0)), getLines(o), 0))) ]]
- [[ formatLang(get_amount_total(o)) or '' ]] [[get_company_currency_symbol()]]
+ [[ formatLang(get_amount_total(o), currency_obj= user.company_id.currency_id) or '' ]]
- [[ formatLang(get_amount_total_in_currency(o)) or '' ]] [[get_company_currency_symbol()]]
+ [[ formatLang(get_amount_total_in_currency(o), currency_obj= user.company_id.currency_id) or '' ]]
diff --git a/addons/account_payment/report/payment_order.py b/addons/account_payment/report/payment_order.py
index 6872bbe3516..5f9708f876c 100644
--- a/addons/account_payment/report/payment_order.py
+++ b/addons/account_payment/report/payment_order.py
@@ -32,7 +32,6 @@ class payment_order(report_sxw.rml_parse):
'time': time,
'get_invoice_name': self._get_invoice_name,
'get_company_currency': self._get_company_currency,
- 'get_company_currency_symbol': self._get_company_currency_symbol,
'get_amount_total_in_currency': self._get_amount_total_in_currency,
'get_amount_total': self._get_amount_total,
'get_account_name': self._get_account_name,
@@ -72,12 +71,8 @@ class payment_order(report_sxw.rml_parse):
user = pool.get('res.users').browse(self.cr, self.uid, self.uid)
return user.company_id and user.company_id.currency_id and user.company_id.currency_id.symbol or False
- def _get_company_currency_symbol(self):
- pool = pooler.get_pool(self.cr.dbname)
- user = pool.get('res.users').browse(self.cr, self.uid, self.uid)
- return user.company_id and user.company_id.currency_id and user.company_id.currency_id.symbol or False
- def _get_account_name(self, bank_id):
+ def _get_account_name(self,bank_id):
if bank_id:
pool = pooler.get_pool(self.cr.dbname)
value_name = pool.get('res.partner.bank').name_get(self.cr, self.uid, [bank_id])
diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py
index 423026251c2..f97f3f6af3c 100644
--- a/addons/account_voucher/account_voucher.py
+++ b/addons/account_voucher/account_voucher.py
@@ -1070,7 +1070,8 @@ class account_voucher(osv.osv):
if line.amount == line.amount_unreconciled:
if not line.move_line_id.amount_residual:
raise osv.except_osv(_('Wrong bank statement line'),_("You have to delete the bank statement line which the payment was reconciled to manually. Please check the payment of the partner %s by the amount of %s.")%(line.voucher_id.partner_id.name, line.voucher_id.amount))
- currency_rate_difference = line.move_line_id.amount_residual - amount
+ sign = voucher_brw.type in ('payment', 'purchase') and -1 or 1
+ currency_rate_difference = sign * (line.move_line_id.amount_residual - amount)
else:
currency_rate_difference = 0.0
move_line = {
diff --git a/addons/account_voucher/report/account_voucher_print.rml b/addons/account_voucher/report/account_voucher_print.rml
index 923e900887a..9f0f2e1f07e 100644
--- a/addons/account_voucher/report/account_voucher_print.rml
+++ b/addons/account_voucher/report/account_voucher_print.rml
@@ -219,7 +219,7 @@
[[ p['pname'] ]]
Note: If you did not ask for a password reset, you can safely ignore this email.
]]>
-
-
- auth.reset_password.form
- auth.reset_password
-
-
-
-
-
-
- Reset Password
- ir.actions.act_window
- auth.reset_password
- form
- form
- new
-
-
-
diff --git a/addons/auth_reset_password/controllers/__init__.py b/addons/auth_reset_password/controllers/__init__.py
new file mode 100644
index 00000000000..f40f0ee976e
--- /dev/null
+++ b/addons/auth_reset_password/controllers/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
+import main
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_reset_password/controllers/main.py b/addons/auth_reset_password/controllers/main.py
new file mode 100644
index 00000000000..639945cfc09
--- /dev/null
+++ b/addons/auth_reset_password/controllers/main.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+import logging
+
+import werkzeug
+
+import openerp
+from openerp import SUPERUSER_ID
+from openerp.modules.registry import RegistryManager
+
+_logger = logging.getLogger(__name__)
+
+class Controller(openerp.addons.web.http.Controller):
+ _cp_path = '/auth_reset_password'
+
+ @openerp.addons.web.http.httprequest
+ def reset_password(self, req, dbname, login):
+ """ retrieve user, and perform reset password """
+ url = '/'
+ registry = RegistryManager.get(dbname)
+ with registry.cursor() as cr:
+ try:
+ res_users = registry.get('res.users')
+ res_users.reset_password(cr, SUPERUSER_ID, login)
+ cr.commit()
+ message = 'An email has been sent with credentials to reset your password'
+ except Exception as e:
+ # signup error
+ _logger.exception('error when resetting password')
+ message = e.message
+ url = "/#action=login&error_message=%s" % werkzeug.urls.url_quote(message)
+ return werkzeug.utils.redirect(url)
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_reset_password/res_users.py b/addons/auth_reset_password/res_users.py
new file mode 100644
index 00000000000..b1039031812
--- /dev/null
+++ b/addons/auth_reset_password/res_users.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
+from openerp.osv import osv, fields
+from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+
+from datetime import datetime, timedelta
+
+def now(**kwargs):
+ dt = datetime.now() + timedelta(**kwargs)
+ return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+
+
+class res_users(osv.osv):
+ _inherit = 'res.users'
+
+ def reset_password(self, cr, uid, login, context=None):
+ """ retrieve the user corresponding to login (login or email),
+ and reset their password
+ """
+ user_ids = self.search(cr, uid, [('login', '=', login)], context=context)
+ if not user_ids:
+ user_ids = self.search(cr, uid, [('email', '=', login)], context=context)
+ if len(user_ids) != 1:
+ raise Exception('Reset password: invalid username or email')
+ return self.action_reset_password(cr, uid, user_ids, context=context)
+
+ def action_reset_password(self, cr, uid, ids, context=None):
+ """ create signup token for each user, and send their signup url by email """
+ # prepare reset password signup
+ res_partner = self.pool.get('res.partner')
+ partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context)]
+ res_partner.signup_prepare(cr, uid, partner_ids, expiration=now(days=+1), context=context)
+
+ # send email to users with their signup url
+ template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_reset_password', 'reset_password_email')
+ assert template._name == 'email.template'
+ for user in self.browse(cr, uid, ids, context):
+ self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, context=context)
+
+ return True
diff --git a/addons/auth_reset_password/res_users_view.xml b/addons/auth_reset_password/res_users_view.xml
new file mode 100644
index 00000000000..9952162ae65
--- /dev/null
+++ b/addons/auth_reset_password/res_users_view.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ user.form.reset_password
+ res.users
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/auth_reset_password/static/src/css/reset_password.css b/addons/auth_reset_password/static/src/css/reset_password.css
deleted file mode 100644
index dd590365918..00000000000
--- a/addons/auth_reset_password/static/src/css/reset_password.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.openerp .oe_login .oe_login_pane {
- height: 152px;
-}
-.openerp .oe_login .oe_login_pane ul.oe_login_switch a {
- color: #eeeeee;
- margin: 0 8px;
-}
-.openerp .oe_login .oe_login_pane ul.oe_login_switch a:hover {
- text-decoration: underline;
-}
-
-
diff --git a/addons/auth_reset_password/static/src/js/reset_password.js b/addons/auth_reset_password/static/src/js/reset_password.js
index 4e09b5c5318..13fe2a39ce5 100644
--- a/addons/auth_reset_password/static/src/js/reset_password.js
+++ b/addons/auth_reset_password/static/src/js/reset_password.js
@@ -1,72 +1,30 @@
openerp.auth_reset_password = function(instance) {
var _t = instance.web._t;
+
instance.web.Login.include({
start: function() {
- var $e = this.$el;
- $e.find('a.oe_login_switch').click(function() {
- $e.find('ul.oe_login_switch').toggle();
- var $m = $e.find('form input[name=is_reset_pw]');
- $m.attr('checked', !$m.is(':checked'));
- });
+ this.$('a.oe_reset_password').click(this.do_reset_password);
return this._super();
},
- on_submit: function(ev) {
- if(ev) {
+ do_reset_password: function(ev) {
+ if (ev) {
ev.preventDefault();
}
-
- var $e = this.$el;
- var db = $e.find("form [name=db]").val();
+ var db = this.$("form [name=db]").val();
+ var login = this.$("form input[name=login]").val();
if (!db) {
- this.do_warn(_t("Login"), _t("No database selected !"));
+ this.do_warn("Login", "No database selected !");
+ return false;
+ } else if (!login) {
+ this.do_warn("Login", "Please enter a username or email address.")
return false;
}
- var $m = $e.find('form input[name=is_reset_pw]');
- if ($m.is(':checked')) {
- var email = $e.find('form input[name=email]').val();
- return this.do_reset_password(db, email);
- } else {
- return this._super(ev);
- }
- },
- do_reset_password: function(db, email) {
- var self = this;
- instance.session.session_authenticate(db, 'anonymous', 'anonymous', true).pipe(function () {
- var func = new instance.web.Model("res.users").get_func("send_reset_password_request");
- return func(email).then(function(res) {
- // show message
- self.do_notify(_t('Reset Password'), _.str.sprintf(_t('We have sent an email to %s with further instructions'), email), true);
- }, function(error, event) {
- // no traceback please
- event.preventDefault();
- });
- }).fail(function(error, event) {
- // cannot log as anonymous or reset_password not installed
- self.do_warn(_t('Reset Password'), _.str.sprintf(_t('Reset Password functionnality is not available for database %s'), db), true);
- });
+ var params = {
+ dbname : db,
+ login: login,
+ };
+ var url = "/auth_reset_password/reset_password?" + $.param(params);
+ window.location = url;
}
});
-
- instance.reset_password = {};
- instance.reset_password.ResetPassword = instance.web.Widget.extend({
- init: function(parent, params) {
- this._super(parent);
- this.token = (params && params.token) || false;
- },
- start: function() {
- this.do_action({
- name: 'Reset Password',
- type: 'ir.actions.act_window',
- context: {default_token: this.token},
- res_model: 'auth.reset_password',
- target: 'new',
- views: [[false, 'form']]
- });
- }
- });
-
-
- instance.web.client_actions.add("reset_password", "instance.reset_password.ResetPassword");
-
-
};
diff --git a/addons/auth_reset_password/static/src/xml/reset_password.xml b/addons/auth_reset_password/static/src/xml/reset_password.xml
index e57334b9c15..841f3906622 100644
--- a/addons/auth_reset_password/static/src/xml/reset_password.xml
+++ b/addons/auth_reset_password/static/src/xml/reset_password.xml
@@ -1,26 +1,11 @@
-
+
-
- // addClass does not work :(
- this.attr('class', (this.attr('class') || '') + ' oe_login_switch');
-
-
+
diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py
index 9cfe89cdc1c..9cbeb60638f 100644
--- a/addons/auth_signup/__openerp__.py
+++ b/addons/auth_signup/__openerp__.py
@@ -34,7 +34,9 @@ Allow users to sign up.
'data': [
'auth_signup_data.xml',
'res_config.xml',
+ 'res_users_view.xml',
],
'js': ['static/src/js/auth_signup.js'],
+ 'css' : ['static/src/css/base.css'],
'qweb': ['static/src/xml/auth_signup.xml'],
}
diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py
index 821ef44f459..54dcc5b4362 100644
--- a/addons/auth_signup/controllers/main.py
+++ b/addons/auth_signup/controllers/main.py
@@ -1,35 +1,62 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
import logging
-import werkzeug.urls
+import werkzeug
+import openerp
from openerp.modules.registry import RegistryManager
from openerp.addons.web.controllers.main import login_and_redirect
-import openerp.addons.web.common.http as openerpweb
-from openerp import SUPERUSER_ID
_logger = logging.getLogger(__name__)
-class OpenIDController(openerpweb.Controller):
+class Controller(openerp.addons.web.http.Controller):
_cp_path = '/auth_signup'
- @openerpweb.httprequest
- def signup(self, req, dbname, name, login, password):
+ @openerp.addons.web.http.jsonrequest
+ def retrieve(self, req, dbname, token):
+ """ retrieve the user info (name, login or email) corresponding to a signup token """
+ registry = RegistryManager.get(dbname)
+ user_info = None
+ with registry.cursor() as cr:
+ res_partner = registry.get('res.partner')
+ user_info = res_partner.signup_retrieve_info(cr, openerp.SUPERUSER_ID, token)
+ return user_info
+
+ @openerp.addons.web.http.httprequest
+ def signup(self, req, dbname, token, name, login, password, state=''):
+ """ sign up a user (new or existing), and log it in """
url = '/'
registry = RegistryManager.get(dbname)
with registry.cursor() as cr:
try:
- Users = registry.get('res.users')
- credentials = Users.auth_signup(cr, SUPERUSER_ID, name, login, password)
+ res_users = registry.get('res.users')
+ values = {'name': name, 'login': login, 'password': password}
+ credentials = res_users.signup(cr, openerp.SUPERUSER_ID, values, token)
cr.commit()
- return login_and_redirect(req, *credentials)
- except AttributeError:
- # auth_signup is not installed
- _logger.exception('attribute error when signup')
- url = "/#action=auth_signup&error=NA" # Not Available
- except Exception:
+ return login_and_redirect(req, *credentials, redirect_url='/#%s'%state)
+ except Exception as e:
# signup error
_logger.exception('error when signup')
- url = "/#action=auth_signup&error=UE" # Unexcpected Error
+ url = "/#action=login&error_message=%s" % werkzeug.urls.url_quote(e.message)
return werkzeug.utils.redirect(url)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py
index 8bfe1fc3ddb..21fce19745c 100644
--- a/addons/auth_signup/res_config.py
+++ b/addons/auth_signup/res_config.py
@@ -20,22 +20,27 @@
##############################################################################
from openerp.osv import osv, fields
+from openerp.tools.safe_eval import safe_eval
class base_config_settings(osv.TransientModel):
_inherit = 'base.config.settings'
_columns = {
- 'auth_signup_uninvited': fields.boolean('Allow public users to sign up', help="If unchecked only invited users may sign up"),
+ 'auth_signup_uninvited': fields.boolean('Allow external users to sign up', help="If unchecked, only invited users may sign up"),
'auth_signup_template_user_id': fields.many2one('res.users', 'Template user for new users created through signup'),
}
def get_default_auth_signup_template_user_id(self, cr, uid, fields, context=None):
icp = self.pool.get('ir.config_parameter')
+ # we use safe_eval on the result, since the value of the parameter is a nonempty string
return {
- 'auth_signup_template_user_id': icp.get_param(cr, uid, 'auth_signup.template_user_id', 0) or False
+ 'auth_signup_uninvited': safe_eval(icp.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')),
+ 'auth_signup_template_user_id': safe_eval(icp.get_param(cr, uid, 'auth_signup.template_user_id', 'False')),
}
def set_auth_signup_template_user_id(self, cr, uid, ids, context=None):
config = self.browse(cr, uid, ids[0], context=context)
icp = self.pool.get('ir.config_parameter')
- icp.set_param(cr, uid, 'auth_signup.template_user_id', config.auth_signup_template_user_id.id)
+ # we store the repr of the values, since the value of the parameter is a required string
+ icp.set_param(cr, uid, 'auth_signup.allow_uninvited', repr(config.auth_signup_uninvited))
+ icp.set_param(cr, uid, 'auth_signup.template_user_id', repr(config.auth_signup_template_user_id.id))
diff --git a/addons/auth_signup/res_config.xml b/addons/auth_signup/res_config.xml
index c17634ececb..9d3eb4eaa5a 100644
--- a/addons/auth_signup/res_config.xml
+++ b/addons/auth_signup/res_config.xml
@@ -14,7 +14,9 @@
-
+
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 5f6463508b6..59196c4da87 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -1,47 +1,201 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+import random
+import time
+import urllib
+import urlparse
+
import openerp
-from openerp.osv import osv
+from openerp.osv import osv, fields
from openerp import SUPERUSER_ID
+from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+from openerp.tools.safe_eval import safe_eval
+
+def random_token():
+ # the token has an entropy of about 120 bits (6 bits/char * 20 chars)
+ chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ return ''.join(random.choice(chars) for i in xrange(20))
+
+def now():
+ return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+
+class res_partner(osv.Model):
+ _inherit = 'res.partner'
+
+ def _get_signup_valid(self, cr, uid, ids, name, arg, context=None):
+ dt = now()
+ res = {}
+ for partner in self.browse(cr, uid, ids, context):
+ res[partner.id] = bool(partner.signup_token) and \
+ (not partner.signup_expiration or dt <= partner.signup_expiration)
+ return res
+
+ def _get_signup_url(self, cr, uid, ids, name, arg, context=None):
+ """ determine a signup url for a given partner """
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+
+ # if required, make sure that every partner without user has a valid signup token
+ if context and context.get('signup_valid'):
+ unsigned_ids = [p.id for p in self.browse(cr, uid, ids, context) if not p.user_ids]
+ self.signup_prepare(cr, uid, unsigned_ids, context=context)
+
+ res = dict.fromkeys(ids, False)
+ for partner in self.browse(cr, uid, ids, context):
+ if partner.signup_token:
+ params = (urllib.quote(cr.dbname), urllib.quote(partner.signup_token))
+ res[partner.id] = urlparse.urljoin(base_url, "#action=login&db=%s&token=%s" % params)
+ elif partner.user_ids:
+ user = partner.user_ids[0]
+ params = (urllib.quote(cr.dbname), urllib.quote(user.login))
+ res[partner.id] = urlparse.urljoin(base_url, "#action=login&db=%s&login=%s" % params)
+ return res
+
+ _columns = {
+ 'signup_token': fields.char(size=24, string='Signup Token'),
+ 'signup_expiration': fields.datetime(string='Signup Expiration'),
+ 'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'),
+ 'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'),
+ }
+
+ def action_signup_prepare(self, cr, uid, ids, context=None):
+ return self.signup_prepare(cr, uid, ids, context=context)
+
+ def signup_prepare(self, cr, uid, ids, expiration=False, context=None):
+ """ generate a new token for the partners with the given validity, if necessary
+ :param expiration: the expiration datetime of the token (string, optional)
+ """
+ for partner in self.browse(cr, uid, ids, context):
+ if expiration or not partner.signup_valid:
+ token = random_token()
+ while self._signup_retrieve_partner(cr, uid, token, context=context):
+ token = random_token()
+ partner.write({'signup_token': token, 'signup_expiration': expiration})
+ return True
+
+ def _signup_retrieve_partner(self, cr, uid, token,
+ check_validity=False, raise_exception=False, context=None):
+ """ find the partner corresponding to a token, and possibly check its validity
+ :param token: the token to resolve
+ :param check_validity: if True, also check validity
+ :param raise_exception: if True, raise exception instead of returning False
+ :return: partner (browse record) or False (if raise_exception is False)
+ """
+ partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context)
+ if not partner_ids:
+ if raise_exception:
+ raise Exception("Signup token '%s' is not valid" % token)
+ return False
+ partner = self.browse(cr, uid, partner_ids[0], context)
+ if check_validity and not partner.signup_valid:
+ if raise_exception:
+ raise Exception("Signup token '%s' is no longer valid" % token)
+ return False
+ return partner
+
+ def signup_retrieve_info(self, cr, uid, token, context=None):
+ """ retrieve the user info about the token
+ :return: a dictionary with the user information:
+ - 'db': the name of the database
+ - 'token': the token, if token is valid
+ - 'name': the name of the partner, if token is valid
+ - 'login': the user login, if the user already exists
+ - 'email': the partner email, if the user does not exist
+ """
+ partner = self._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None)
+ res = {'db': cr.dbname}
+ if partner.signup_valid:
+ res['token'] = token
+ res['name'] = partner.name
+ if partner.user_ids:
+ res['login'] = partner.user_ids[0].login
+ else:
+ res['email'] = partner.email or ''
+ return res
class res_users(osv.Model):
_inherit = 'res.users'
- def auth_signup_create(self, cr, uid, new_user, context=None):
- # new_user:
- # login
- # email
- # name (optional)
- # partner_id (optional)
- # groups (optional)
- # sign (for partner_id and groups)
- #
- user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth.signup_template_user_id', 0)
- if user_template_id:
- self.pool.get('res.users').copy(cr, SUPERUSER_ID, user_template_id, new_user, context=context)
- else:
- self.pool.get('res.users').create(cr, SUPERUSER_ID, new_user, context=context)
+ def _get_state(self, cr, uid, ids, name, arg, context=None):
+ return dict((user.id, 'new' if not user.login_date else 'reset' if user.signup_token else 'active')
+ for user in self.browse(cr, uid, ids, context))
- def auth_signup(self, cr, uid, name, login, password, context=None):
- r = (cr.dbname, login, password)
- res = self.search(cr, uid, [("login", "=", login)])
- if res:
- # Existing user
- user_id = res[0]
- try:
- self.check(cr.dbname, user_id, password)
- # Same password
- except openerp.exceptions.AccessDenied:
- # Different password
- raise
- else:
- # New user
- new_user = {
- 'name': name,
- 'login': login,
- 'user_email': login,
- 'password': password,
- 'active': True,
- }
- self.auth_signup_create(cr, uid, new_user)
- return r
+ _columns = {
+ 'state': fields.function(_get_state, string='State', type='selection',
+ selection=[('new', 'New'), ('active', 'Active'), ('reset', 'Resetting Password')]),
+ }
-#
+ def signup(self, cr, uid, values, token=None, context=None):
+ """ signup a user, to either:
+ - create a new user (no token), or
+ - create a user for a partner (with token, but no user for partner), or
+ - change the password of a user (with token, and existing user).
+ :param values: a dictionary with field values
+ :param token: signup token (optional)
+ :return: (dbname, login, password) for the signed up user
+ """
+ assert values.get('login') and values.get('password')
+ result = (cr.dbname, values['login'], values['password'])
+
+ if token:
+ # signup with a token: find the corresponding partner id
+ res_partner = self.pool.get('res.partner')
+ partner = res_partner._signup_retrieve_partner(cr, uid, token,
+ check_validity=True, raise_exception=True, context=None)
+ # invalidate signup token
+ partner.write({'signup_token': False, 'signup_expiration': False})
+ if partner.user_ids:
+ # user exists, modify its password
+ partner.user_ids[0].write({'password': values['password']})
+ else:
+ # user does not exist: sign up invited user
+ self._signup_create_user(cr, uid, {
+ 'name': partner.name,
+ 'login': values['login'],
+ 'password': values['password'],
+ 'email': values['login'],
+ 'partner_id': partner.id,
+ }, context=context)
+ return result
+
+ # sign up an external user
+ assert values.get('name'), 'Signup: no name given for new user'
+ self._signup_create_user(cr, uid, {
+ 'name': values['name'],
+ 'login': values['login'],
+ 'password': values['password'],
+ 'email': values['login'],
+ }, context=context)
+ return result
+
+ def _signup_create_user(self, cr, uid, values, context=None):
+ """ create a new user from the template user """
+ ir_config_parameter = self.pool.get('ir.config_parameter')
+ template_user_id = safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False'))
+ assert template_user_id and self.exists(cr, uid, template_user_id, context=context), 'Signup: invalid template user'
+
+ # check that uninvited users may sign up
+ if 'partner_id' not in values:
+ if not safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')):
+ raise Exception('Signup is not allowed for uninvited users')
+
+ # create a copy of the template user (attached to a specific partner_id if given)
+ values['active'] = True
+ return self.copy(cr, uid, template_user_id, values, context=context)
diff --git a/addons/auth_signup/res_users_view.xml b/addons/auth_signup/res_users_view.xml
new file mode 100644
index 00000000000..c42e1ab5f41
--- /dev/null
+++ b/addons/auth_signup/res_users_view.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ user.form.state
+ res.users
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/auth_signup/static/src/css/Makefile b/addons/auth_signup/static/src/css/Makefile
new file mode 100644
index 00000000000..d6b4f4b2fc8
--- /dev/null
+++ b/addons/auth_signup/static/src/css/Makefile
@@ -0,0 +1,3 @@
+base.css: base.sass
+ sass --trace -t expanded base.sass base.css
+
diff --git a/addons/auth_signup/static/src/css/base.css b/addons/auth_signup/static/src/css/base.css
new file mode 100644
index 00000000000..3f97e7a9301
--- /dev/null
+++ b/addons/auth_signup/static/src/css/base.css
@@ -0,0 +1,10 @@
+@charset "utf-8";
+.openerp .oe_login .oe_signup_show {
+ display: none;
+}
+.openerp .oe_login_signup .oe_signup_show {
+ display: block !important;
+}
+.openerp .oe_login_signup .oe_signup_hide {
+ display: none;
+}
diff --git a/addons/auth_signup/static/src/css/base.sass b/addons/auth_signup/static/src/css/base.sass
new file mode 100644
index 00000000000..f97665d192c
--- /dev/null
+++ b/addons/auth_signup/static/src/css/base.sass
@@ -0,0 +1,13 @@
+@charset "utf-8"
+
+.openerp
+ // Regular login form
+ .oe_login
+ .oe_signup_show
+ display: none
+ // Signup form
+ .oe_login_signup
+ .oe_signup_show
+ display: block !important
+ .oe_signup_hide
+ display: none
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index 2ab13aa7598..0974b73292d 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -5,56 +5,100 @@ openerp.auth_signup = function(instance) {
instance.web.Login.include({
start: function() {
var self = this;
- this.$('a.oe_signup').click(function() {
- var dbname = self.$("form [name=db]").val();
- self.do_action({
- type: 'ir.actions.client',
- tag: 'auth_signup.signup',
- params: {'dbname': dbname},
- target: 'new',
- name: 'Sign up'
- });
- return true;
+ var d = this._super();
+
+ // to switch between the signup and regular login form
+ this.$('a.oe_signup_signup').click(function() {
+ self.$el.addClass("oe_login_signup");
+ });
+ this.$('a.oe_signup_back').click(function() {
+ self.$el.removeClass("oe_login_signup");
+ delete self.params.token;
});
- return this._super();
- },
- });
+ // if there is an error message in params, show it then forget it
+ if (self.params.error_message) {
+ this.show_error(self.params.error_message);
+ delete self.params.error_message;
+ }
- instance.auth_signup.Signup = instance.web.Widget.extend({
- template: 'auth_signup.signup',
- init: function(parent, params) {
- this.params = params;
- return this._super();
+ // in case of a signup, retrieve the user information from the token
+ if (self.params.db && self.params.token) {
+ d = self.rpc("/auth_signup/retrieve", {dbname: self.params.db, token: self.params.token})
+ .done(self.on_token_loaded)
+ .fail(self.on_token_failed);
+ }
+ return d;
},
- start: function() {
- var self = this;
- this.$('input[name=password_confirmation]').keyup(function() {
- var v = $(this).val();
- var $b = self.$('button');
- if (_.isEmpty(v) || self.$('input[name=password]').val() === v) {
- $b.removeAttr('disabled');
+ on_token_loaded: function(result) {
+ // select the right the database
+ this.selected_db = result.db;
+ this.on_db_loaded({db_list: [result.db]});
+ if (result.token) {
+ // switch to signup mode, set user name and login
+ this.$el.addClass("oe_login_signup");
+ this.$("form input[name=name]").val(result.name).attr("readonly", "readonly");
+ if (result.login) {
+ this.$("form input[name=login]").val(result.login).attr("readonly", "readonly");
} else {
- $b.attr('disabled', 'disabled');
+ this.$("form input[name=login]").val(result.email);
}
- });
-
- this.$('form').submit(function(ev) {
- if(ev) {
- ev.preventDefault();
+ } else {
+ // remain in login mode, set login if present
+ delete this.params.token;
+ this.$("form input[name=login]").val(result.login || "");
+ }
+ },
+ on_token_failed: function(result, ev) {
+ if (ev) {
+ ev.preventDefault();
+ }
+ this.show_error("Invalid signup token");
+ delete this.params.db;
+ delete this.params.token;
+ },
+ on_submit: function(ev) {
+ if (ev) {
+ ev.preventDefault();
+ }
+ if (this.$el.hasClass("oe_login_signup")) {
+ // signup user (or reset password)
+ var db = this.$("form [name=db]").val();
+ var name = this.$("form input[name=name]").val();
+ var login = this.$("form input[name=login]").val();
+ var password = this.$("form input[name=password]").val();
+ var confirm_password = this.$("form input[name=confirm_password]").val();
+ if (!db) {
+ this.do_warn("Login", "No database selected !");
+ return false;
+ } else if (!name) {
+ this.do_warn("Login", "Please enter a name.")
+ return false;
+ } else if (!login) {
+ this.do_warn("Login", "Please enter a username.")
+ return false;
+ } else if (!password || !confirm_password) {
+ this.do_warn("Login", "Please enter a password and confirm it.")
+ return false;
+ } else if (password !== confirm_password) {
+ this.do_warn("Login", "Passwords do not match; please retype them.")
+ return false;
}
var params = {
- dbname : self.params.dbname,
- name: self.$('input[name=name]').val(),
- login: self.$('input[name=email]').val(),
- password: self.$('input[name=password]').val(),
+ dbname : db,
+ token: this.params.token || "",
+ name: name,
+ login: login,
+ password: password,
+ state: $.param(this.params)
};
var url = "/auth_signup/signup?" + $.param(params);
window.location = url;
- });
- return this._super();
- }
+ } else {
+ // regular login
+ this._super(ev);
+ }
+ },
});
- instance.web.client_actions.add("auth_signup.signup", "instance.auth_signup.Signup");
};
diff --git a/addons/auth_signup/static/src/xml/auth_signup.xml b/addons/auth_signup/static/src/xml/auth_signup.xml
index c2eec846d15..e63bd5863b0 100644
--- a/addons/auth_signup/static/src/xml/auth_signup.xml
+++ b/addons/auth_signup/static/src/xml/auth_signup.xml
@@ -1,28 +1,28 @@
-
+
-
-
-
-
-
-
diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py
index 5b8c41787d4..f5fb3dbd051 100644
--- a/addons/base_calendar/base_calendar.py
+++ b/addons/base_calendar/base_calendar.py
@@ -39,11 +39,11 @@ months = {
def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
"""
- Get recurrent dates based on Rule string considering exdate and start date
- @param rrulestring: Rulestring
- @param exdate: List of exception dates for rrule
- @param startdate: Startdate for computing recurrent dates
- @return: List of Recurrent dates
+ Get recurrent dates based on Rule string considering exdate and start date.
+ @param rrulestring: rulestring
+ @param exdate: list of exception dates for rrule
+ @param startdate: startdate for computing recurrent dates
+ @return: list of Recurrent dates
"""
def todate(date):
val = parser.parse(''.join((re.compile('\d')).findall(date)))
@@ -67,11 +67,12 @@ def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
"""
- This function converts virtual event id into real id of actual event
- @param base_calendar_id: Id of calendar
- @param with_date: If value passed to this param it will return dates based on value of withdate + base_calendar_id
+ Convert a "virtual/recurring event id" (type string) into a real event id (type int).
+ E.g. virtual/recurring event id is 4-20091201100000, so it will return 4.
+ @param base_calendar_id: id of calendar
+ @param with_date: if a value is passed to this param it will return dates based on value of withdate + base_calendar_id
+ @return: real event id
"""
-
if base_calendar_id and isinstance(base_calendar_id, (str, unicode)):
res = base_calendar_id.split('-')
@@ -89,12 +90,13 @@ def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
def real_id2base_calendar_id(real_id, recurrent_date):
"""
- Convert real id of record into virtual id using recurrent_date
- e.g. real id is 1 and recurrent_date is 01-12-2009 10:00:00 then it will return
- 1-20091201100000
- @return: real id with recurrent date.
+ Convert a real event id (type int) into a "virtual/recurring event id" (type string).
+ E.g. real event id is 1 and recurrent_date is set to 01-12-2009 10:00:00, so
+ it will return 1-20091201100000.
+ @param real_id: real event id
+ @param recurrent_date: real event recurrent date
+ @return: string containing the real id and the recurrent date
"""
-
if real_id and recurrent_date:
recurrent_date = time.strftime("%Y%m%d%H%M%S", \
time.strptime(recurrent_date, "%Y-%m-%d %H:%M:%S"))
@@ -104,10 +106,10 @@ def real_id2base_calendar_id(real_id, recurrent_date):
def _links_get(self, cr, uid, context=None):
"""
Get request link.
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param context: A standard dictionary for contextual values
- @return: list of dictionary which contain object and name and id.
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param context: a standard dictionary for contextual values
+ @return: list of dictionary which contain object and name and id
"""
obj = self.pool.get('res.request.link')
ids = obj.search(cr, uid, [])
@@ -216,9 +218,9 @@ class calendar_attendee(osv.osv):
def _get_address(self, name=None, email=None):
"""
- Gives email information in ical CAL-ADDRESS type format
- @param name: Name for CAL-ADDRESS value
- @param email: Email address for CAL-ADDRESS value
+ Gives email information in ical CAL-ADDRESS type format.
+ @param name: name for CAL-ADDRESS value
+ @param email: email address for CAL-ADDRESS value
"""
if name and email:
name += ':'
@@ -226,13 +228,13 @@ class calendar_attendee(osv.osv):
def _compute_data(self, cr, uid, ids, name, arg, context=None):
"""
- Compute data on function fields for attendee values .
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar attendee’s IDs.
- @param name: name of field.
- @param context: A standard dictionary for contextual values
- @return: Dictionary of form {id: {'field Name': value'}}.
+ Compute data on function fields for attendee values.
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param ids: list of calendar attendee's IDs
+ @param name: name of field
+ @param context: a standard dictionary for contextual values
+ @return: dictionary of form {id: {'field Name': value'}}
"""
name = name[0]
result = {}
@@ -297,10 +299,10 @@ class calendar_attendee(osv.osv):
def _links_get(self, cr, uid, context=None):
"""
Get request link for ref field in calendar attendee.
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's id for security checks
@param context: A standard dictionary for contextual values
- @return: list of dictionary which contain object and name and id.
+ @return: list of dictionary which contain object and name and id
"""
obj = self.pool.get('res.request.link')
ids = obj.search(cr, uid, [])
@@ -310,10 +312,10 @@ class calendar_attendee(osv.osv):
def _lang_get(self, cr, uid, context=None):
"""
Get language for language selection field.
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param context: A standard dictionary for contextual values
- @return: list of dictionary which contain code and name and id.
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's id for security checks
+ @param context: a standard dictionary for contextual values
+ @return: list of dictionary which contain code and name and id
"""
obj = self.pool.get('res.lang')
ids = obj.search(cr, uid, [])
@@ -375,7 +377,6 @@ property or property parameter."),
'ref': fields.reference('Event Ref', selection=_links_get, size=128),
'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"),
}
-
_defaults = {
'state': 'needs-action',
'role': 'req-participant',
@@ -388,12 +389,12 @@ property or property parameter."),
def get_ics_file(self, cr, uid, event_obj, context=None):
"""
- Returns iCalendar file for the event invitation
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param event_obj: Event object (browse record)
- @param context: A standard dictionary for contextual values
+ Returns iCalendar file for the event invitation.
+ @param self: the object pointer
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's id for security checks
+ @param event_obj: event object (browse record)
+ @param context: a standard dictionary for contextual values
@return: .ics file content
"""
res = None
@@ -471,7 +472,7 @@ property or property parameter."),
def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context=None):
"""
Send mail for event invitation to event attendees.
- @param email_from: Email address for user sending the mail
+ @param email_from: email address for user sending the mail
@return: True
"""
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name
@@ -517,11 +518,11 @@ property or property parameter."),
def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
"""
Make entry on email and availbility on change of user_id field.
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar attendee’s IDs.
- @param user_id: Changed value of User id
- @return: dictionary of value. which put value in email and availability fields.
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param ids: list of calendar attendee's IDs
+ @param user_id: changed value of User id
+ @return: dictionary of values which put value in email and availability fields
"""
if not user_id:
@@ -531,24 +532,25 @@ property or property parameter."),
return {'value': {'email': user.email, 'availability':user.availability}}
def do_tentative(self, cr, uid, ids, context=None, *args):
- """ Makes event invitation as Tentative
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar attendee’s IDs
- @param *args: Get Tupple value
- @param context: A standard dictionary for contextual values
+ """
+ Makes event invitation as Tentative.
+ @param self: the object pointer
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param ids: list of calendar attendee's IDs
+ @param *args: get Tupple value
+ @param context: a standard dictionary for contextual values
"""
return self.write(cr, uid, ids, {'state': 'tentative'}, context)
def do_accept(self, cr, uid, ids, context=None, *args):
"""
- Update state of invitation as Accepted and
- if the invited user is other then event user it will make a copy of this event for invited user
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar attendee’s IDs.
- @param context: A standard dictionary for contextual values
+ Update state of invitation as Accepted and if the invited user is other
+ then event user it will make a copy of this event for invited user.
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param ids: list of calendar attendee's IDs
+ @param context: a standard dictionary for contextual values
@return: True
"""
if context is None:
@@ -564,25 +566,28 @@ property or property parameter."),
return True
def do_decline(self, cr, uid, ids, context=None, *args):
- """ Marks event invitation as Declined
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar attendee’s IDs
- @param *args: Get Tupple value
- @param context: A standard dictionary for contextual values """
+ """
+ Marks event invitation as Declined.
+ @param self: the object pointer
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param ids: list of calendar attendee's IDs
+ @param *args: get Tupple value
+ @param context: a standard dictionary for contextual values
+ """
if context is None:
context = {}
return self.write(cr, uid, ids, {'state': 'declined'}, context)
def create(self, cr, uid, vals, context=None):
- """ Overrides orm create method.
+ """
+ Overrides orm create method.
@param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param vals: Get Values
- @param context: A standard dictionary for contextual values """
-
+ @param cr: the current row, from the database cursor
+ @param uid: the current user's ID for security checks
+ @param vals: get Values
+ @param context: a standard dictionary for contextual values
+ """
if context is None:
context = {}
if not vals.get("email") and vals.get("cn"):
@@ -592,6 +597,7 @@ property or property parameter."),
vals['cn'] = vals.get("cn")
res = super(calendar_attendee, self).create(cr, uid, vals, context)
return res
+
calendar_attendee()
class res_alarm(osv.osv):
@@ -630,8 +636,8 @@ true, it will allow you to hide the event alarm information without removing it.
"""
Create Alarm for event.
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of res alarm’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of res alarm's IDs.
@param model: Model name.
@param date: Event date
@param context: A standard dictionary for contextual values
@@ -704,8 +710,8 @@ true, it will allow you to hide the event alarm information without removing it.
"""
Delete alarm specified in ids
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of res alarm’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of res alarm's IDs.
@param model: Model name for which alarm is to be cleared.
@return: True
"""
@@ -715,12 +721,12 @@ true, it will allow you to hide the event alarm information without removing it.
ir_obj = self.pool.get('ir.model')
model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0]
model_obj = self.pool.get(model)
- for datas in model_obj.browse(cr, uid, ids, context=context):
- alarm_ids = alarm_obj.search(cr, uid, [('model_id', '=', model_id), ('res_id', '=', datas.id)])
+ for data in model_obj.browse(cr, uid, ids, context=context):
+ alarm_ids = alarm_obj.search(cr, uid, [('model_id', '=', model_id), ('res_id', '=', data.id)])
if alarm_ids:
alarm_obj.unlink(cr, uid, alarm_ids)
cr.execute('Update %s set base_calendar_alarm_id=NULL, alarm_id=NULL\
- where id=%%s' % model_obj._table,(datas.id,))
+ where id=%%s' % model_obj._table,(data.id,))
return True
res_alarm()
@@ -773,7 +779,7 @@ class calendar_alarm(osv.osv):
Overrides orm create method.
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param vals: dictionary of fields value.{‘name_of_the_field’: value, ...}
+ @param vals: dictionary of fields value.{'name_of_the_field': value, ...}
@param context: A standard dictionary for contextual values
@return: new record id for calendar_alarm.
"""
@@ -798,8 +804,8 @@ class calendar_alarm(osv.osv):
"""Scheduler for event reminder
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar alarm’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of calendar alarm's IDs.
@param use_new_cursor: False or the dbname
@param context: A standard dictionary for contextual values
"""
@@ -891,8 +897,8 @@ class calendar_event(osv.osv):
"""Returns duration and/or end date based on values passed
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar event’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of calendar event's IDs.
@param start_date: Starting date
@param duration: Duration between start date and end date
@param end_date: Ending Datee
@@ -967,14 +973,14 @@ class calendar_event(osv.osv):
if not isinstance(ids, list):
ids = [ids]
- for datas in self.read(cr, uid, ids, ['id','byday','recurrency', 'month_list','end_date', 'rrule_type', 'select1', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'exrule', 'day', 'week_list' ], context=context):
- event = datas['id']
- if datas.get('interval', 0) < 0:
+ for data in self.read(cr, uid, ids, ['id','byday','recurrency', 'month_list','end_date', 'rrule_type', 'select1', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'exrule', 'day', 'week_list' ], context=context):
+ event = data['id']
+ if data.get('interval', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Interval cannot be negative.'))
- if datas.get('count', 0) <= 0:
+ if data.get('count', 0) <= 0:
raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.'))
- if datas['recurrency']:
- result[event] = self.compute_rule_string(datas)
+ if data['recurrency']:
+ result[event] = self.compute_rule_string(data)
else:
result[event] = ""
return result
@@ -990,7 +996,6 @@ class calendar_event(osv.osv):
super(calendar_event, obj).write(cr, uid, ids, data, context=context)
return True
-
_columns = {
'id': fields.integer('ID', readonly=True),
'sequence': fields.integer('Sequence'),
@@ -1006,21 +1011,24 @@ class calendar_event(osv.osv):
'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], \
'Show Time as', states={'done': [('readonly', True)]}),
'base_calendar_url': fields.char('Caldav URL', size=264),
- 'state': fields.selection([('tentative', 'Tentative'),
- ('cancelled', 'Cancelled'),
- ('confirmed', 'Confirmed'),
- ], 'Status', readonly=True),
+ 'state': fields.selection([
+ ('tentative', 'Tentative'),
+ ('cancelled', 'Cancelled'),
+ ('confirmed', 'Confirmed'),
+ ], 'Status', readonly=True),
'exdate': fields.text('Exception Date/Times', help="This property \
defines the list of date/time exceptions for a recurring calendar component."),
'exrule': fields.char('Exception Rule', size=352, help="Defines a \
rule or repeating pattern of time to exclude from the recurring rule."),
'rrule': fields.function(_get_rulestring, type='char', size=124, \
fnct_inv=_rrule_write, store=True, string='Recurrent Rule'),
- 'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \
- ('weekly', 'Weekly'), ('monthly', 'Monthly'), \
- ('yearly', 'Yearly'),],
- 'Recurrency', states={'done': [('readonly', True)]},
- help="Let the event automatically repeat at that interval"),
+ 'rrule_type': fields.selection([
+ ('daily', 'Daily'),
+ ('weekly', 'Weekly'),
+ ('monthly', 'Monthly'),
+ ('yearly', 'Yearly')
+ ], 'Recurrency', states={'done': [('readonly', True)]},
+ help="Let the event automatically repeat at that interval"),
'alarm_id': fields.many2one('res.alarm', 'Reminder', states={'done': [('readonly', True)]},
help="Set an alarm at this time, before the event occurs" ),
'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'),
@@ -1028,7 +1036,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
'recurrent_id': fields.datetime('Recurrent ID date'),
'vtimezone': fields.selection(_tz_get, size=64, string='Timezone'),
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
- 'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with Organizer Attribure of VEvent.
+ 'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with organizer attribute of VEvent.
'organizer_id': fields.many2one('res.users', 'Organizer', states={'done': [('readonly', True)]}),
'end_type' : fields.selection([('count', 'Number of repetitions'), ('end_date','End date')], 'Recurrence Termination'),
'interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"),
@@ -1043,13 +1051,21 @@ rule or repeating pattern of time to exclude from the recurring rule."),
'select1': fields.selection([('date', 'Date of month'),
('day', 'Day of month')], 'Option'),
'day': fields.integer('Date of month'),
- 'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \
- ('WE', 'Wednesday'), ('TH', 'Thursday'), \
- ('FR', 'Friday'), ('SA', 'Saturday'), \
- ('SU', 'Sunday')], 'Weekday'),
- 'byday': fields.selection([('1', 'First'), ('2', 'Second'), \
- ('3', 'Third'), ('4', 'Fourth'), \
- ('5', 'Fifth'), ('-1', 'Last')], 'By day'),
+ 'week_list': fields.selection([
+ ('MO', 'Monday'),
+ ('TU', 'Tuesday'),
+ ('WE', 'Wednesday'),
+ ('TH', 'Thursday'),
+ ('FR', 'Friday'),
+ ('SA', 'Saturday'),
+ ('SU', 'Sunday')], 'Weekday'),
+ 'byday': fields.selection([
+ ('1', 'First'),
+ ('2', 'Second'),
+ ('3', 'Third'),
+ ('4', 'Fourth'),
+ ('5', 'Fifth'),
+ ('-1', 'Last')], 'By day'),
'month_list': fields.selection(months.items(), 'Month'),
'end_date': fields.date('Repeat Until'),
'attendee_ids': fields.many2many('calendar.attendee', 'event_attendee_rel', \
@@ -1101,9 +1117,9 @@ rule or repeating pattern of time to exclude from the recurring rule."),
return res
_defaults = {
- 'end_type' : 'count',
- 'count' : 1,
- 'rrule_type' : 'none',
+ 'end_type': 'count',
+ 'count': 1,
+ 'rrule_type': False,
'state': 'tentative',
'class': 'public',
'show_as': 'busy',
@@ -1113,7 +1129,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
'user_id': lambda self, cr, uid, ctx: uid,
'organizer': default_organizer,
}
-
+
def _check_closing_date(self, cr, uid, ids, context=None):
for event in self.browse(cr, uid, ids, context=context):
if event.date_deadline < event.date:
@@ -1129,7 +1145,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
This method gives ids of dates that comes between start date and end date of calendar views
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param limit: The Number of Results to Return """
if not context:
context = {}
@@ -1140,7 +1156,8 @@ rule or repeating pattern of time to exclude from the recurring rule."),
result.append(data['id'])
continue
event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
-# To check: If the start date is replace by event date .. the event date will be changed by that of calendar code
+
+ # TOCHECK: the start date should be replaced by event date; the event date will be changed by that of calendar code
if not data['rrule']:
continue
@@ -1186,45 +1203,46 @@ rule or repeating pattern of time to exclude from the recurring rule."),
ids = list(set(result))
return ids
- def compute_rule_string(self, datas):
+ def compute_rule_string(self, data):
"""
Compute rule string according to value type RECUR of iCalendar from the values given.
@param self: the object pointer
- @param datas: dictionary of freq and interval value.
+ @param data: dictionary of freq and interval value
+ @return: string containing recurring rule (empty if no rule)
"""
- def get_week_string(freq, datas):
+ def get_week_string(freq, data):
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
if freq == 'weekly':
- byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
+ byday = map(lambda x: x.upper(), filter(lambda x: data.get(x) and x in weekdays, data))
if byday:
return ';BYDAY=' + ','.join(byday)
return ''
- def get_month_string(freq, datas):
+ def get_month_string(freq, data):
if freq == 'monthly':
- if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
+ if data.get('select1')=='date' and (data.get('day') < 1 or data.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select a proper day of the month."))
- if datas.get('select1')=='day':
- return ';BYDAY=' + datas.get('byday') + datas.get('week_list')
- elif datas.get('select1')=='date':
- return ';BYMONTHDAY=' + str(datas.get('day'))
+ if data.get('select1')=='day':
+ return ';BYDAY=' + data.get('byday') + data.get('week_list')
+ elif data.get('select1')=='date':
+ return ';BYMONTHDAY=' + str(data.get('day'))
return ''
- def get_end_date(datas):
- if datas.get('end_date'):
- datas['end_date_new'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + 'T235959Z'
+ def get_end_date(data):
+ if data.get('end_date'):
+ data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('end_date'))) + 'T235959Z'
- return (datas.get('end_type') == 'count' and (';COUNT=' + str(datas.get('count'))) or '') +\
- ((datas.get('end_date_new') and datas.get('end_type') == 'end_date' and (';UNTIL=' + datas.get('end_date_new'))) or '')
+ return (data.get('end_type') == 'count' and (';COUNT=' + str(data.get('count'))) or '') +\
+ ((data.get('end_date_new') and data.get('end_type') == 'end_date' and (';UNTIL=' + data.get('end_date_new'))) or '')
- freq=datas.get('rrule_type')
- if freq == 'none':
- return ''
+ freq = data.get('rrule_type', False)
+ res = ''
+ if freq:
+ interval_srting = data.get('interval') and (';INTERVAL=' + str(data.get('interval'))) or ''
+ res = 'FREQ=' + freq.upper() + get_week_string(freq, data) + interval_srting + get_end_date(data) + get_month_string(freq, data)
- interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
-
- return 'FREQ=' + freq.upper() + get_week_string(freq, datas) + interval_srting + get_end_date(datas) + get_month_string(freq, datas)
+ return res
def _get_empty_rrule_data(self):
return {
@@ -1248,19 +1266,6 @@ rule or repeating pattern of time to exclude from the recurring rule."),
'week_list' : False
}
- #def _write_rrule(self, cr, uid, ids, field_value, rule_date=False, context=None):
- # data = self._get_empty_rrule_data()
- #
- # if field_value:
- # data['recurrency'] = True
- # for event in self.browse(cr, uid, ids, context=context):
- # rdate = rule_date or event.date
- # update_data = self._parse_rrule(field_value, dict(data), rdate)
- # data.update(update_data)
- # #parse_rrule
- # self.write(cr, uid, event.id, data, context=context)
-
-
def _parse_rrule(self, rule, data, date_start):
day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
rrule_type = ['yearly', 'monthly', 'weekly', 'daily']
@@ -1278,7 +1283,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
if i in r._byweekday:
data[day_list[i]] = True
data['rrule_type'] = 'weekly'
- #repeat monthly bynweekday ((weekday, weeknumber), )
+ #repeat monthly by nweekday ((weekday, weeknumber), )
if r._bynweekday:
data['week_list'] = day_list[r._bynweekday[0][0]].upper()
data['byday'] = r._bynweekday[0][1]
@@ -1290,7 +1295,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
data['select1'] = 'date'
data['rrule_type'] = 'monthly'
- #yearly but for openerp it's monthly, take same information as monthly but interval is 12 times
+ #repeat yearly but for openerp it's monthly, take same information as monthly but interval is 12 times
if r._bymonth:
data['interval'] = data['interval'] * 12
@@ -1363,7 +1368,6 @@ rule or repeating pattern of time to exclude from the recurring rule."),
except Exception:
return True
-
def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
context = context or {}
if isinstance(ids, (str, int, long)):
@@ -1384,14 +1388,14 @@ rule or repeating pattern of time to exclude from the recurring rule."),
data = self.read(cr, uid, event_id, ['date', 'date_deadline', \
'rrule', 'duration', 'exdate'])
if data.get('rrule'):
- data.update(vals)
- data.update({
- 'recurrent_uid': real_event_id,
- 'recurrent_id': data.get('date'),
- 'rrule_type': 'none',
- 'rrule': '',
- 'recurrency' : False,
- })
+ data.update(
+ vals,
+ recurrent_uid=real_event_id,
+ recurrent_id=data.get('date'),
+ rrule_type=False,
+ rrule='',
+ recurrency=False,
+ )
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
@@ -1435,7 +1439,6 @@ rule or repeating pattern of time to exclude from the recurring rule."),
return res
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
- # FIXME This whole id mangling has to go!
if context is None:
context = {}
fields2 = fields and fields[:] or None
@@ -1445,10 +1448,12 @@ rule or repeating pattern of time to exclude from the recurring rule."),
if fields and (f not in fields):
fields2.append(f)
+ # FIXME This whole id mangling has to go!
if isinstance(ids, (str, int, long)):
select = [ids]
else:
select = ids
+
select = map(lambda x: (x, base_calendar_id2real_id(x)), select)
result = []
@@ -1477,7 +1482,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
if f not in ('id','date','date_deadline','duration','user_id','state'):
if isinstance(r[f], list):
r[f] = []
- else:
+ else:
r[f] = False
if f=='name':
r[f] = _('Busy')
@@ -1525,7 +1530,6 @@ rule or repeating pattern of time to exclude from the recurring rule."),
self.unlink_events(cr, uid, ids, context=context)
return res
-
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
@@ -1543,7 +1547,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
""" Makes event invitation as Tentative
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param ids: List of Event IDs
@param *args: Get Tupple value
@param context: A standard dictionary for contextual values
@@ -1554,7 +1558,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
""" Makes event invitation as Tentative
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param ids: List of Event IDs
@param *args: Get Tupple value
@param context: A standard dictionary for contextual values
@@ -1565,7 +1569,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
""" Makes event invitation as Tentative
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param ids: List of Event IDs
@param *args: Get Tupple value
@param context: A standard dictionary for contextual values
@@ -1586,9 +1590,9 @@ class calendar_todo(osv.osv):
Get Date
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param ids: List of calendar todo's IDs.
- @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
+ @param args: list of tuples of form [(‘name_of_the_field', ‘operator', value), ...].
@param context: A standard dictionary for contextual values
"""
@@ -1602,10 +1606,10 @@ class calendar_todo(osv.osv):
Set Date
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param id: calendar's ID.
@param value: Get Value
- @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
+ @param args: list of tuples of form [('name_of_the_field', 'operator', value), ...].
@param context: A standard dictionary for contextual values
"""
@@ -1633,7 +1637,7 @@ class ir_values(osv.osv):
Set IR Values
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param model: Get The Model
"""
@@ -1652,7 +1656,7 @@ class ir_values(osv.osv):
Get IR Values
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param model: Get The Model
"""
if context is None:
@@ -1678,8 +1682,8 @@ class ir_model(osv.osv):
Overrides orm read method.
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of IR Model’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of IR Model's IDs.
@param context: A standard dictionary for contextual values
"""
new_ids = isinstance(ids, (str, int, long)) and [ids] or ids
@@ -1696,24 +1700,24 @@ ir_model()
class virtual_report_spool(web_services.report_spool):
- def exp_report(self, db, uid, object, ids, datas=None, context=None):
+ def exp_report(self, db, uid, object, ids, data=None, context=None):
"""
Export Report
@param self: The object pointer
@param db: get the current database,
- @param uid: the current user’s ID for security checks,
+ @param uid: the current user's ID for security checks,
@param context: A standard dictionary for contextual values
"""
if object == 'printscreen.list':
return super(virtual_report_spool, self).exp_report(db, uid, \
- object, ids, datas, context)
+ object, ids, data, context)
new_ids = []
for id in ids:
new_ids.append(base_calendar_id2real_id(id))
- if datas.get('id', False):
- datas['id'] = base_calendar_id2real_id(datas['id'])
- return super(virtual_report_spool, self).exp_report(db, uid, object, new_ids, datas, context)
+ if data.get('id', False):
+ data['id'] = base_calendar_id2real_id(data['id'])
+ return super(virtual_report_spool, self).exp_report(db, uid, object, new_ids, data, context)
virtual_report_spool()
@@ -1725,8 +1729,8 @@ class res_users(osv.osv):
Get User Availability
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of res user’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of res user's IDs.
@param context: A standard dictionary for contextual values
"""
@@ -1755,8 +1759,8 @@ class res_users(osv.osv):
Get User Availability Function
@param self: The object pointer
@param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of res user’s IDs.
+ @param uid: the current user's ID for security checks,
+ @param ids: List of res user's IDs.
@param context: A standard dictionary for contextual values
"""
@@ -1770,5 +1774,4 @@ class res_users(osv.osv):
res_users()
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml
index fe292a489ab..d5dc75df873 100644
--- a/addons/base_calendar/base_calendar_view.xml
+++ b/addons/base_calendar/base_calendar_view.xml
@@ -1,8 +1,7 @@
-
-
+
calendar.attendee.formcalendar.attendee
@@ -55,8 +54,7 @@
-
-
+
calendar.attendee.treecalendar.attendee
@@ -73,8 +71,7 @@
-
-
+
calendar.attendee.searchcalendar.attendee
@@ -110,18 +107,16 @@
{'default_sent_by_uid': uid}
-
+
-
-
-
+
res.alarm.formres.alarm
@@ -140,8 +135,7 @@
-
-
+
res.alarm.treeres.alarm
@@ -171,15 +165,13 @@
-
-
+
-
-
+
Event Formcalendar.event
@@ -334,8 +326,7 @@
-
-
+
Event Treecalendar.event
@@ -352,8 +343,7 @@
-
-
+
Events Calendarcalendar.event
@@ -367,8 +357,7 @@
-
-
+
Calendar Events Searchcalendar.event
@@ -393,8 +382,7 @@
-
-
+
Eventsir.actions.act_window
@@ -404,8 +392,7 @@
-
-
+
diff --git a/addons/base_calendar/crm_meeting.py b/addons/base_calendar/crm_meeting.py
index b87114f3a66..9ceaea671ca 100644
--- a/addons/base_calendar/crm_meeting.py
+++ b/addons/base_calendar/crm_meeting.py
@@ -43,14 +43,14 @@ class crm_meeting(base_state, osv.Model):
_name = 'crm.meeting'
_description = "Meeting"
_order = "id desc"
- _inherit = ["calendar.event", "mail.thread", 'ir.needaction_mixin']
+ _inherit = ["calendar.event", "mail.thread", "ir.needaction_mixin"]
_columns = {
# base_state required fields
'create_date': fields.datetime('Creation Date', readonly=True),
'write_date': fields.datetime('Write Date', readonly=True),
'date_open': fields.datetime('Confirmed', readonly=True),
'date_closed': fields.datetime('Closed', readonly=True),
- 'partner_ids': fields.many2many('res.partner', 'crm_meeting_partner_rel', 'meeting_id','partner_id',
+ 'partner_ids': fields.many2many('res.partner', 'crm_meeting_partner_rel', 'meeting_id', 'partner_id',
string='Attendees', states={'done': [('readonly', True)]}),
'state': fields.selection(
[('draft', 'Unconfirmed'), ('open', 'Confirmed')],
@@ -115,5 +115,3 @@ class crm_meeting(base_state, osv.Model):
def case_close_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Meeting completed."), context=context)
-
-
diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml
index adcfc23a520..4df0a8a22b2 100644
--- a/addons/base_calendar/crm_meeting_view.xml
+++ b/addons/base_calendar/crm_meeting_view.xml
@@ -15,9 +15,9 @@
action_crm_meeting_read
-
+ action
-
+ crm.meetingclient_action_multi
@@ -33,9 +33,9 @@
action_crm_meeting_unread
-
+ action
-
+ crm.meetingclient_action_multi
@@ -73,7 +73,7 @@
@@ -569,10 +569,10 @@
-
+
-
+
@@ -580,9 +580,8 @@
domain="['|', ('section_id.user_id','=',uid), ('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
help="Opportunities that are assigned to either me or one of the sale teams I manage" />
-
-
+
diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml
index 35ed9411215..a17633d0ef6 100644
--- a/addons/crm/report/crm_lead_report_view.xml
+++ b/addons/crm/report/crm_lead_report_view.xml
@@ -8,7 +8,7 @@
crm.lead.report.treecrm.lead.report
-
+
@@ -130,7 +130,7 @@
crm.lead.report.treecrm.lead.report
-
+
diff --git a/addons/crm/report/crm_phonecall_report_view.xml b/addons/crm/report/crm_phonecall_report_view.xml
index ec7ea5e6bdb..a2120aef6ab 100644
--- a/addons/crm/report/crm_phonecall_report_view.xml
+++ b/addons/crm/report/crm_phonecall_report_view.xml
@@ -8,7 +8,7 @@
crm.phonecall.report.treecrm.phonecall.report
-
+
diff --git a/addons/crm/res_config.py b/addons/crm/res_config.py
index b04853d9f6a..6f431b90cba 100644
--- a/addons/crm/res_config.py
+++ b/addons/crm/res_config.py
@@ -32,6 +32,13 @@ class crm_configuration(osv.osv_memory):
'group_fund_raising': fields.boolean("Manage Fund Raising",
implied_group='crm.group_fund_raising',
help="""Allows you to trace and manage your activities for fund raising."""),
+ 'module_crm_claim':fields.boolean("Manage Customer Claims",
+ help="""Allows you to track your customers/suppliers claims and grievances.
+ This installs the module crm_claim."""),
+ 'module_crm_helpdesk':fields.boolean("Manage Helpdesk and Support",
+ help="""Allows you to communicate with Customer, process Customer query, and provide better help and support. This installs the module crm_helpdesk."""),
+
+
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/crm/res_config_view.xml b/addons/crm/res_config_view.xml
index 6330d7a0500..9534c8620ee 100644
--- a/addons/crm/res_config_view.xml
+++ b/addons/crm/res_config_view.xml
@@ -7,6 +7,22 @@
sale.config.settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml
index 84a0bd4cf49..b7aa334a9c3 100644
--- a/addons/crm/res_partner_view.xml
+++ b/addons/crm/res_partner_view.xml
@@ -15,31 +15,12 @@
-
- view.res.partner.tree.crm.inherited2
- res.partner
-
-
-
-
-
-
-
-
-
view.res.partner.search.crm.inherited3res.partner
-
-
-
-
-
-
-
diff --git a/addons/crm_claim/__openerp__.py b/addons/crm_claim/__openerp__.py
index f5ab836401b..dfc549412d4 100644
--- a/addons/crm_claim/__openerp__.py
+++ b/addons/crm_claim/__openerp__.py
@@ -25,8 +25,10 @@
'version': '1.0',
'category': 'Customer Relationship Management',
'description': """
-This modules allows you to track your customers/suppliers claims and grievances.
+
+Manage Customer Claims.
================================================================================
+This application allows you to track your customers/suppliers claims and grievances.
It is fully integrated with the email gateway so that you can create
automatically new claims based on incoming emails.
diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py
index cbf3ec9f13a..c88b2fe26af 100644
--- a/addons/crm_claim/crm_claim.py
+++ b/addons/crm_claim/crm_claim.py
@@ -26,6 +26,7 @@ from osv import fields, osv
import time
import tools
from tools.translate import _
+from tools import html2plaintext
CRM_CLAIM_PENDING_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
@@ -192,9 +193,10 @@ class crm_claim(base_stage, osv.osv):
This override updates the document according to the email.
"""
if custom_values is None: custom_values = {}
+ desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
'name': msg.get('subject') or _("No Subject"),
- 'description': msg.get('body'),
+ 'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
})
diff --git a/addons/crm_claim/report/crm_claim_report_view.xml b/addons/crm_claim/report/crm_claim_report_view.xml
index 5a3a3d92a09..a61490c6086 100644
--- a/addons/crm_claim/report/crm_claim_report_view.xml
+++ b/addons/crm_claim/report/crm_claim_report_view.xml
@@ -8,7 +8,7 @@
crm.claim.report.treecrm.claim.report
-
+
diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py
index f36a3b72640..ceffd282aa8 100644
--- a/addons/crm_helpdesk/crm_helpdesk.py
+++ b/addons/crm_helpdesk/crm_helpdesk.py
@@ -25,6 +25,7 @@ from crm import crm
from osv import fields, osv
import tools
from tools.translate import _
+from tools import html2plaintext
CRM_HELPDESK_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
@@ -104,9 +105,10 @@ class crm_helpdesk(base_state, base_stage, osv.osv):
This override updates the document according to the email.
"""
if custom_values is None: custom_values = {}
+ desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
'name': msg.get('subject') or _("No Subject"),
- 'description': msg.get('body'),
+ 'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
diff --git a/addons/crm_helpdesk/report/crm_helpdesk_report_view.xml b/addons/crm_helpdesk/report/crm_helpdesk_report_view.xml
index 4750fa3344d..05f502656d2 100644
--- a/addons/crm_helpdesk/report/crm_helpdesk_report_view.xml
+++ b/addons/crm_helpdesk/report/crm_helpdesk_report_view.xml
@@ -8,7 +8,7 @@
crm.helpdesk.report.treecrm.helpdesk.report
-
+
diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml
index cd2e3f08c9a..2cffb6fb899 100644
--- a/addons/crm_partner_assign/crm_lead_view.xml
+++ b/addons/crm_partner_assign/crm_lead_view.xml
@@ -56,7 +56,7 @@
-
+
diff --git a/addons/crm_partner_assign/report/crm_lead_report_view.xml b/addons/crm_partner_assign/report/crm_lead_report_view.xml
index 662a7c3dce0..ee9266a0235 100644
--- a/addons/crm_partner_assign/report/crm_lead_report_view.xml
+++ b/addons/crm_partner_assign/report/crm_lead_report_view.xml
@@ -65,7 +65,7 @@
crm.lead.assign.report.treecrm.lead.report.assign
-
+
diff --git a/addons/crm_partner_assign/report/crm_partner_report_view.xml b/addons/crm_partner_assign/report/crm_partner_report_view.xml
index 107328ac4e0..eb2c86874c4 100644
--- a/addons/crm_partner_assign/report/crm_partner_report_view.xml
+++ b/addons/crm_partner_assign/report/crm_partner_report_view.xml
@@ -41,7 +41,7 @@
crm.partner.assign.report.treecrm.partner.report.assign
-
+
diff --git a/addons/document/document.py b/addons/document/document.py
index 3ae783f49e7..37a0219d143 100644
--- a/addons/document/document.py
+++ b/addons/document/document.py
@@ -36,8 +36,7 @@ DMS_ROOT_PATH = tools.config.get('document_path', os.path.join(tools.config['roo
class document_file(osv.osv):
_inherit = 'ir.attachment'
- _rec_name = 'datas_fname'
-
+ _rec_name = 'name'
def _attach_parent_id(self, cr, uid, ids=None, context=None):
"""Migrate ir.attachments to the document module.
@@ -149,29 +148,12 @@ class document_file(osv.osv):
_sql_constraints = [
# filename_uniq is not possible in pure SQL
]
- def _check_duplication(self, cr, uid, vals, ids=None, op='create'):
- name = vals.get('name', False)
- parent_id = vals.get('parent_id', False)
- res_model = vals.get('res_model', False)
- res_id = vals.get('res_id', 0)
- if op == 'write':
- for file in self.browse(cr, uid, ids): # FIXME fields_only
- if not name:
- name = file.name
- if not parent_id:
- parent_id = file.parent_id and file.parent_id.id or False
- if not res_model:
- res_model = file.res_model and file.res_model or False
- if not res_id:
- res_id = file.res_id and file.res_id or 0
- res = self.search(cr, uid, [('id', '<>', file.id), ('name', '=', name), ('parent_id', '=', parent_id), ('res_model', '=', res_model), ('res_id', '=', res_id)])
- if len(res):
- return False
- if op == 'create':
- res = self.search(cr, uid, [('name', '=', name), ('parent_id', '=', parent_id), ('res_id', '=', res_id), ('res_model', '=', res_model)])
- if len(res):
- return False
- return True
+
+ def onchange_file(self, cr, uid, ids, datas_fname=False, context=None):
+ res = {'value':{}}
+ if datas_fname:
+ res['value'].update({'name': datas_fname})
+ return res
def check(self, cr, uid, ids, mode, context=None, values=None):
"""Check access wrt. res_model, relax the rule of ir.attachment parent
@@ -219,8 +201,6 @@ class document_file(osv.osv):
res = self.search(cr, uid, [('id', 'in', ids)])
if not len(res):
return False
- if not self._check_duplication(cr, uid, vals, ids, 'write'):
- raise osv.except_osv(_('ValidateError'), _('File name must be unique!'))
# if nodes call this write(), they must skip the code below
from_node = context and context.get('__from_node', False)
@@ -286,22 +266,8 @@ class document_file(osv.osv):
else:
if vals.get('file_size'):
del vals['file_size']
- result = self._check_duplication(cr, uid, vals)
- if not result:
- domain = [
- ('res_id', '=', vals['res_id']),
- ('res_model', '=', vals['res_model']),
- ('datas_fname', '=', vals['datas_fname']),
- ]
- attach_ids = self.search(cr, uid, domain, context=context)
- super(document_file, self).write(cr, uid, attach_ids,
- {'datas' : vals['datas']},
- context=context)
- result = attach_ids[0]
- else:
- #raise osv.except_osv(_('ValidateError'), _('File name must be unique!'))
- result = super(document_file, self).create(cr, uid, vals, context)
- return result
+
+ return super(document_file, self).create(cr, uid, vals, context)
def __get_partner_id(self, cr, uid, res_model, res_id, context=None):
""" A helper to retrieve the associated partner from any res_model+id
diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml
index b631583fb27..54776c4d36e 100644
--- a/addons/document/document_view.xml
+++ b/addons/document/document_view.xml
@@ -228,6 +228,9 @@
+
+
+
@@ -338,15 +341,6 @@
-
- ir.attachment.view.inherit
- ir.attachment
-
-
-
-
-
- process.node.form
diff --git a/addons/document_page/document_page_view.xml b/addons/document_page/document_page_view.xml
index 63f208c08a7..d8daaada967 100644
--- a/addons/document_page/document_page_view.xml
+++ b/addons/document_page/document_page_view.xml
@@ -55,7 +55,7 @@