From 6ae73d53113e6e22765dd953126e3db351d2a283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 19 Mar 2012 18:03:32 +0100 Subject: [PATCH 1/6] [FIX] mail.thread: added _fields_id='res_id' in args: allow to manually defined the _fields_id relationship of a one2many column; for this function field this is not a standard arg, but additional arg bzr revid: tde@openerp.com-20120319170332-xkadjpddlocnh37m --- addons/mail/mail_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c6ac11731cd..addfac7d29c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -73,7 +73,7 @@ class mail_thread(osv.osv): # OpenSocial: message_ids_social is a dummy field that should not be used _columns = { 'message_ids_social': fields.function(_get_message_ids, method=True, - type='one2many', obj='mail.message', string='Temp messages'), + type='one2many', obj='mail.message', string='Temp messages', _fields_id = 'res_id'), } #------------------------------------------------------ From 3a1e1ce480ce2d525e75d7ab8864386e0e298408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 20 Mar 2012 19:09:17 +0100 Subject: [PATCH 2/6] [IMP] share: update module to accept an 'invite' action, that is alike share, but used for OpenSocial. Share now also adds the 'invite' feature to OpenSocial. bzr revid: tde@openerp.com-20120320180917-gj2c0tetyerfuprr --- addons/share/static/src/js/share.js | 8 ++++- addons/share/static/src/xml/share.xml | 7 ++++ addons/share/wizard/share_wizard.py | 40 ++++++++++++++++----- addons/share/wizard/share_wizard_view.xml | 42 +++++++++++++++++++++++ 4 files changed, 87 insertions(+), 10 deletions(-) diff --git a/addons/share/static/src/js/share.js b/addons/share/static/src/js/share.js index a4dfba12e28..4d543b001db 100644 --- a/addons/share/static/src/js/share.js +++ b/addons/share/static/src/js/share.js @@ -1,7 +1,7 @@ openerp.share = function(session) { -function launch_wizard(self, view, user_type) { +function launch_wizard(self, view, user_type, subscribe) { var action = view.getParent().action; var Share = new session.web.DataSet(self, 'share.wizard', view.dataset.get_context()); var domain = new session.web.CompoundDomain(view.dataset.domain); @@ -18,6 +18,7 @@ function launch_wizard(self, view, user_type) { action_id: action.id, user_type: user_type || 'embedded', view_type: view.fields_view.type, + subscribe: subscribe || false, }, function(result) { var share_id = result.result; var step1 = Share.call('go_step_1', [[share_id],], function(result) { @@ -68,6 +69,7 @@ session.web.ViewManagerAction.include({ has_share(function() { self.$element.find('a.oe-share_link').click(self.on_click_share_link); self.$element.find('a.oe-share').click(self.on_click_share); + self.$element.delegate('a.oe-share-mail', 'click', self.on_click_share_mail); }, function() { self.$element.find('a.oe-share_link').remove(); self.$element.find('a.oe-share').remove(); @@ -82,6 +84,10 @@ session.web.ViewManagerAction.include({ e.preventDefault(); launch_wizard(this, this.views[this.active_view].controller, 'emails'); }, + on_click_share_mail: function(e) { + e.preventDefault(); + launch_wizard(this, this.views[this.active_view].controller, 'emails', true); + }, }); }; diff --git a/addons/share/static/src/xml/share.xml b/addons/share/static/src/xml/share.xml index 532e8c74c9d..1d62b38ee7c 100644 --- a/addons/share/static/src/xml/share.xml +++ b/addons/share/static/src/xml/share.xml @@ -10,4 +10,11 @@ + + + + + + + diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index 09fb43ca361..83fdf88473c 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -165,6 +165,10 @@ class share_wizard(osv.osv_memory): 'user_type': fields.selection(lambda s, *a, **k: s._user_type_selection(*a, **k),'Sharing method', required=True, help="Select the type of user(s) you would like to share data with."), 'new_users': fields.text("Emails"), + 'email_1': fields.char('New user email', size=64), + 'email_2': fields.char('New user email', size=64), + 'email_3': fields.char('New user email', size=64), + 'invite': fields.boolean('Invite users to OpenSocial record'), 'access_mode': fields.selection([('readonly','Can view'),('readwrite','Can edit')],'Access Mode', required=True, help="Access rights to be granted on the shared documents."), 'result_line_ids': fields.one2many('share.wizard.result.line', 'share_wizard_id', 'Summary', readonly=True), @@ -181,6 +185,7 @@ class share_wizard(osv.osv_memory): _defaults = { 'view_type': 'page', 'user_type' : 'embedded', + 'invite': False, 'domain': lambda self, cr, uid, context, *a: context.get('domain', '[]'), 'action_id': lambda self, cr, uid, context, *a: context.get('action_id'), 'access_mode': 'readonly', @@ -192,11 +197,14 @@ class share_wizard(osv.osv_memory): return bool(self.pool.get('res.users').browse(cr, uid, uid, context=context).user_email) def go_step_1(self, cr, uid, ids, context=None): - user_type = self.browse(cr,uid,ids,context)[0].user_type - if user_type == 'emails' and not self.has_email(cr, uid, context=context): + wizard_data = self.browse(cr,uid,ids,context)[0] + if wizard_data.user_type == 'emails' and not self.has_email(cr, uid, context=context): raise osv.except_osv(_('No e-mail address configured'), _('You must configure your e-mail address in the user preferences before using the Share button.')) - model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1') + if wizard_data.invite: + model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1_mail') + else: + model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1') action = self.pool.get(model).read(cr, uid, res_id, context=context) action['res_id'] = ids[0] action.pop('context', '') @@ -223,12 +231,18 @@ class share_wizard(osv.osv_memory): created_ids = [] existing_ids = [] if wizard_data.user_type == 'emails': - for new_user in (wizard_data.new_users or '').split('\n'): + # get new user list from email data + new_users = (wizard_data.new_users or '').split('\n') + new_users += [wizard_data.email_1 or '', wizard_data.email_2 or '', wizard_data.email_3 or ''] + for new_user in new_users: # Ignore blank lines new_user = new_user.strip() if not new_user: continue # Ignore the user if it already exists. - existing = user_obj.search(cr, UID_ROOT, [('login', '=', new_user)]) + if not wizard_data.invite: + existing = user_obj.search(cr, UID_ROOT, [('login', '=', new_user)]) + else: + existing = user_obj.search(cr, UID_ROOT, [('user_email', '=', new_user)]) existing_ids.extend(existing) if existing: new_line = { 'user_id': existing[0], @@ -643,7 +657,7 @@ class share_wizard(osv.osv_memory): _('You must be a member of the Share/User group to use the share wizard'), context=context) if wizard_data.user_type == 'emails': - self._assert(wizard_data.new_users, + self._assert((wizard_data.new_users or wizard_data.email_1 or wizard_data.email_2 or wizard_data.email_3), _('Please indicate the emails of the persons to share with, one per line'), context=context) @@ -667,21 +681,21 @@ class share_wizard(osv.osv_memory): if new_ids: # new users need a new shortcut AND a home action self._setup_action_and_shortcut(cr, uid, wizard_data, new_ids, make_home=True, context=context) - return group_id + return group_id, new_ids, existing_ids def go_step_2(self, cr, uid, ids, context=None): wizard_data = self.browse(cr, uid, ids[0], context=context) self._check_preconditions(cr, uid, wizard_data, context=context) # Create shared group and users - group_id = self._create_share_users_group(cr, uid, wizard_data, context=context) + group_id, new_ids, existing_ids = self._create_share_users_group(cr, uid, wizard_data, context=context) current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) model_obj = self.pool.get('ir.model') model_id = model_obj.search(cr, uid, [('model','=', wizard_data.action_id.res_model)])[0] model = model_obj.browse(cr, uid, model_id, context=context) - + # ACCESS RIGHTS # We have several classes of objects that should receive different access rights: # Let: @@ -731,6 +745,14 @@ class share_wizard(osv.osv_memory): # refresh wizard_data wizard_data = self.browse(cr, uid, ids[0], context=context) + + # Invite (OpenSocial): automatically subscribe users to the record + res_id = 0 + for cond in safe_eval(main_domain): + if cond[0] == 'id': + res_id = cond[2] + if wizard_data.invite and res_id > 0: + self.pool.get(model.model).message_subscribe(cr, uid, [res_id], new_ids + existing_ids, context=context) # send the confirmation emails: self.send_emails(cr, uid, wizard_data, context=context) diff --git a/addons/share/wizard/share_wizard_view.xml b/addons/share/wizard/share_wizard_view.xml index 29bd5e59f01..27bd39111c0 100644 --- a/addons/share/wizard/share_wizard_view.xml +++ b/addons/share/wizard/share_wizard_view.xml @@ -52,6 +52,37 @@ + + share.step1_mail.form + share.wizard + form + +
+ + + + + + + + + + + + + + + + + + + From 3d6c40394a06e662f4f22bbc0b49c0e1725eaa1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Mar 2012 11:00:52 +0200 Subject: [PATCH 4/6] [IMP] Improved share-invite mechanism. Also slighty refactored code in emails sending. bzr revid: tde@openerp.com-20120328090052-v73mn3tdnjat4kkb --- addons/share/static/src/js/share.js | 7 +- addons/share/wizard/share_wizard.py | 166 +++++++++++++++------- addons/share/wizard/share_wizard_view.xml | 49 +------ 3 files changed, 127 insertions(+), 95 deletions(-) diff --git a/addons/share/static/src/js/share.js b/addons/share/static/src/js/share.js index 54dec1fd6e6..8346b177f8e 100644 --- a/addons/share/static/src/js/share.js +++ b/addons/share/static/src/js/share.js @@ -8,12 +8,15 @@ function launch_wizard(self, view, user_type, invite) { if (view.fields_view.type == 'form') { domain = new session.web.CompoundDomain(domain, [['id', '=', view.datarecord.id]]); } + if (view.fields_view.type == 'form') rec_name = view.datarecord.name; + else rec_name = ''; self.rpc('/web/session/eval_domain_and_context', { domains: [domain], contexts: [view.dataset.context] }, function (result) { Share.create({ name: action.name, + record_name: rec_name, domain: result.domain, action_id: action.id, user_type: user_type || 'embedded', @@ -78,11 +81,11 @@ session.web.ViewManagerAction.include({ }, on_click_share_link: function(e) { e.preventDefault(); - launch_wizard(this, this.views[this.active_view].controller); + launch_wizard(this, this.views[this.active_view].controller, 'embedded', false); }, on_click_share: function(e) { e.preventDefault(); - launch_wizard(this, this.views[this.active_view].controller, 'emails'); + launch_wizard(this, this.views[this.active_view].controller, 'emails', false); }, on_click_share_mail: function(e) { e.preventDefault(); diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index 83fdf88473c..854f021bf67 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -175,6 +175,7 @@ class share_wizard(osv.osv_memory): 'share_root_url': fields.function(_share_root_url, string='Share Access URL', type='char', size=512, readonly=True, help='Main access page for users that are granted shared access'), 'name': fields.char('Share Title', size=64, required=True, help="Title for the share (displayed to users as menu and shortcut name)"), + 'record_name': fields.char('Record name', size=128, help="Name of the shared record, if sharing a precise record"), 'message': fields.text("Personal Message", help="An optional personal message, to be included in the e-mail notification."), 'embed_code': fields.function(_embed_code, type='text'), @@ -201,10 +202,7 @@ class share_wizard(osv.osv_memory): if wizard_data.user_type == 'emails' and not self.has_email(cr, uid, context=context): raise osv.except_osv(_('No e-mail address configured'), _('You must configure your e-mail address in the user preferences before using the Share button.')) - if wizard_data.invite: - model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1_mail') - else: - model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1') + model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1') action = self.pool.get(model).read(cr, uid, res_id, context=context) action['res_id'] = ids[0] action.pop('context', '') @@ -257,6 +255,7 @@ class share_wizard(osv.osv_memory): 'user_email': new_user, 'groups_id': [(6,0,[group_id])], 'share': True, + 'message_email_pref': 'all', 'company_id': current_user.company_id.id }, context) new_line = { 'user_id': user_id, @@ -746,75 +745,140 @@ class share_wizard(osv.osv_memory): # refresh wizard_data wizard_data = self.browse(cr, uid, ids[0], context=context) - # Invite (OpenSocial): automatically subscribe users to the record - res_id = 0 - for cond in safe_eval(main_domain): - if cond[0] == 'id': - res_id = cond[2] - if wizard_data.invite and res_id > 0: + # EMAILS AND NOTIFICATIONS + # A. Not invite: as before + # -> send emails to destination users + # B. Invite (OpenSocial) + # -> subscribe all users (existing and new) to the record + # -> send a notification with a summary to the current record + # -> send a notification to all users; users allowing to receive + # emails in preferences will receive it + # new users by default receive all notifications by email + + # A. + if not wizard_data.invite: + self.send_emails(cr, uid, wizard_data, context=context) + # B. + else: + # Invite (OpenSocial): automatically subscribe users to the record + res_id = 0 + for cond in safe_eval(main_domain): + if cond[0] == 'id': + res_id = cond[2] + # Record id not found: issue + if res_id <= 0: + raise osv.except_osv(_('Record id not found'), _('The share engine has not been able to fetch a record_id for your invitation.')) self.pool.get(model.model).message_subscribe(cr, uid, [res_id], new_ids + existing_ids, context=context) + self.send_invite_email(cr, uid, wizard_data, context=context) + self.send_invite_note(cr, uid, model.model, res_id, wizard_data, context=context) + + # CLOSE + # A. Not invite: as before + # B. Invite: skip summary screen, get back to the record + + # A. + if not wizard_data.invite: + dummy, step2_form_view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'share_step2_form') + return { + 'name': _('Shared access created!'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'share.wizard', + 'view_id': False, + 'res_id': ids[0], + 'views': [(step2_form_view_id, 'form'), (False, 'tree'), (False, 'calendar'), (False, 'graph')], + 'type': 'ir.actions.act_window', + 'target': 'new' + } + # B. + else: + return { + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': model.model, + 'view_id': False, + 'res_id': res_id, + 'views': [(False, 'form'), (False, 'tree'), (False, 'calendar'), (False, 'graph')], + 'type': 'ir.actions.act_window', + } + - # send the confirmation emails: - self.send_emails(cr, uid, wizard_data, context=context) - - dummy, step2_form_view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'share_step2_form') - return { - 'name': _('Shared access created!'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'share.wizard', - 'view_id': False, - 'res_id': ids[0], - 'views': [(step2_form_view_id, 'form'), (False, 'tree'), (False, 'calendar'), (False, 'graph')], - 'type': 'ir.actions.act_window', - 'target': 'new' - } - + def send_invite_note(self, cr, uid, model_name, res_id, wizard_data, context=None): + subject = _('Invitation') + body = 'has been shared with' + tmp_idx = 0 + for result_line in wizard_data.result_line_ids: + body += ' @%s' % (result_line.user_id.login) + if tmp_idx < len(wizard_data.result_line_ids)-2: + body += ',' + elif tmp_idx == len(wizard_data.result_line_ids)-2: + body += ' and' + body += '.' + return self.pool.get(model_name).message_append_note(cr, uid, [res_id], _('System Notification'), body, context=context) + + def send_invite_email(self, cr, uid, wizard_data, context=None): + message_obj = self.pool.get('mail.message') + notification_obj = self.pool.get('mail.notification') + user = self.pool.get('res.users').browse(cr, UID_ROOT, uid) + if not user.user_email: + raise osv.except_osv(_('Email required'), _('The current user must have an email address configured in User Preferences to be able to send outgoing emails.')) + + # TODO: also send an HTML version of this mail + for result_line in wizard_data.result_line_ids: + email_to = result_line.user_id.user_email + if not email_to: + continue + subject = _('Invitation to collaborate about %s') % (wizard_data.record_name) + body = _("Hello,\n\n") + body += _("I have shared %s (%s) with you!\n\n") % (wizard_data.record_name, wizard_data.name) + if wizard_data.message: + body += "%s\n\n" % (wizard_data.message) + if result_line.newly_created: + body += _("The documents are not attached, you can view them online directly on my OpenERP server at:\n %s\n\n") % (result_line.share_url) + body += _("These are your credentials to access this protected area:\n") + body += "%s: %s" % (_("Username"), result_line.user_id.login) + "\n" + body += "%s: %s" % (_("Password"), result_line.password) + "\n" + body += "%s: %s" % (_("Database"), cr.dbname) + "\n" + body += _("The documents have been automatically added to your subscriptions.\n\n") + body += '%s\n\n' % ((user.signature or '')) + body += "--\n" + body += _("OpenERP is a powerful and user-friendly suite of Business Applications (CRM, Sales, HR, etc.)\n" + "It is open source and can be found on http://www.openerp.com.") + msg_id = message_obj.schedule_with_attach(cr, uid, user.user_email, [email_to], subject, body, model='', context=context) + notification_obj.create(cr, uid, {'user_id': result_line.user_id.id, 'message_id': msg_id}, context=context) + def send_emails(self, cr, uid, wizard_data, context=None): self._logger.info('Sending share notifications by email...') mail_message = self.pool.get('mail.message') user = self.pool.get('res.users').browse(cr, UID_ROOT, uid) - + if not user.user_email: + raise osv.except_osv(_('Email required'), _('The current user must have an email address configured in User Preferences to be able to send outgoing emails.')) + # TODO: also send an HTML version of this mail msg_ids = [] for result_line in wizard_data.result_line_ids: email_to = result_line.user_id.user_email if not email_to: continue - if not user.user_email: - raise osv.except_osv(_('Email required'), _('The current user must have an email address configured in User Preferences to be able to send outgoing emails.')) subject = wizard_data.name - body = _("Hello,") - body += "\n\n" - body += _("I've shared %s with you!") % wizard_data.name - body += "\n\n" - body += _("The documents are not attached, you can view them online directly on my OpenERP server at:") - body += "\n " + result_line.share_url - body += "\n\n" + body = _("Hello,\n\n") + body += _("I've shared %s with you!\n\n") % wizard_data.name + body += _("The documents are not attached, you can view them online directly on my OpenERP server at:\n %s\n\n") % (result_line.share_url) if wizard_data.message: - body += wizard_data.message - body += "\n\n" + body += '%s\n\n' % (wizard_data.message) if result_line.newly_created: body += _("These are your credentials to access this protected area:\n") - body += "%s: %s" % (_("Username"), result_line.user_id.login) + "\n" - body += "%s: %s" % (_("Password"), result_line.password) + "\n" - body += "%s: %s" % (_("Database"), cr.dbname) + "\n" + body += "%s: %s\n" % (_("Username"), result_line.user_id.login) + body += "%s: %s\n" % (_("Password"), result_line.password) + body += "%s: %s\n" % (_("Database"), cr.dbname) else: body += _("The documents have been automatically added to your current OpenERP documents.\n") body += _("You may use your current login (%s) and password to view them.\n") % result_line.user_id.login - body += "\n\n" - body += (user.signature or '') - body += "\n\n" + body += "\n\n%s\n\n" % ( (user.signature or '') ) body += "--\n" body += _("OpenERP is a powerful and user-friendly suite of Business Applications (CRM, Sales, HR, etc.)\n" "It is open source and can be found on http://www.openerp.com.") - msg_ids.append(mail_message.schedule_with_attach(cr, uid, - user.user_email, - [email_to], - subject, - body, - model='share.wizard', - context=context)) + msg_ids.append(mail_message.schedule_with_attach(cr, uid, user.user_email, [email_to], subject, body, model='share.wizard', context=context)) # force direct delivery, as users expect instant notification mail_message.send(cr, uid, msg_ids, context=context) self._logger.info('%d share notification(s) sent.', len(msg_ids)) diff --git a/addons/share/wizard/share_wizard_view.xml b/addons/share/wizard/share_wizard_view.xml index 27bd39111c0..77d9213f315 100644 --- a/addons/share/wizard/share_wizard_view.xml +++ b/addons/share/wizard/share_wizard_view.xml @@ -30,47 +30,23 @@
- + + - + - - - - - - - - - - - -