diff --git a/addons/auth_signup/auth_signup_data.xml b/addons/auth_signup/auth_signup_data.xml
index 4a6bf9ae4b9..39b62148494 100644
--- a/addons/auth_signup/auth_signup_data.xml
+++ b/addons/auth_signup/auth_signup_data.xml
@@ -13,7 +13,7 @@
-
+ auth_signup.template_user_id
diff --git a/addons/base_calendar/__openerp__.py b/addons/base_calendar/__openerp__.py
index 48ec05e2e92..b1afacfbd01 100644
--- a/addons/base_calendar/__openerp__.py
+++ b/addons/base_calendar/__openerp__.py
@@ -20,7 +20,7 @@
##############################################################################
{
- 'name': 'Calendar Layer',
+ 'name': 'Calendar',
'version': '1.0',
'depends': ['base', 'base_status', 'mail', 'base_action_rule'],
'description': """
@@ -51,6 +51,7 @@ If you need to manage your meetings, you should install the CRM module.
],
'test' : ['test/base_calendar_test.yml'],
'installable': True,
+ 'application': True,
'auto_install': False,
'certificate': '00694071962960352821',
'images': ['images/base_calendar1.jpeg','images/base_calendar2.jpeg','images/base_calendar3.jpeg','images/base_calendar4.jpeg',],
diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml
index 89066ce74bb..d374c2e6d88 100644
--- a/addons/base_calendar/base_calendar_view.xml
+++ b/addons/base_calendar/base_calendar_view.xml
@@ -418,7 +418,7 @@
ir.actions.act_windowcalendar.eventform
- tree,form,calendar
+ calendar,tree,form
@@ -428,5 +428,6 @@
name="Events" parent="base.menu_calendar_configuration"
sequence="15" action="action_view_event"/>
+
diff --git a/addons/base_calendar/static/src/img/icon.png b/addons/base_calendar/static/src/img/icon.png
new file mode 100644
index 00000000000..f612cd9ea4b
Binary files /dev/null and b/addons/base_calendar/static/src/img/icon.png differ
diff --git a/addons/base_setup/res_partner_view.xml b/addons/base_setup/res_partner_view.xml
index 5e54c0b8629..15b80151831 100644
--- a/addons/base_setup/res_partner_view.xml
+++ b/addons/base_setup/res_partner_view.xml
@@ -9,14 +9,11 @@
-
-
-
-
+
+
-
diff --git a/addons/contacts/__init__.py b/addons/contacts/__init__.py
new file mode 100644
index 00000000000..26c654db9dd
--- /dev/null
+++ b/addons/contacts/__init__.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
diff --git a/addons/contacts/__openerp__.py b/addons/contacts/__openerp__.py
new file mode 100644
index 00000000000..3b4df183478
--- /dev/null
+++ b/addons/contacts/__openerp__.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+{
+ 'name': 'Address Book',
+ 'version': '1.0',
+ 'category': 'Tools',
+ 'description': """
+This module gives you a quick view of your address book, accessible from your home page.
+You can track your suppliers, customers and other contacts.
+""",
+ 'author': 'OpenERP SA',
+ 'website': 'http://openerp.com',
+ 'summary': 'Contacts, People and Companies',
+ 'depends': [
+ 'mail',
+ ],
+ 'data': [
+ 'contacts_view.xml',
+ ],
+ 'installable': True,
+ 'application': True,
+ 'auto_install': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/contacts/contacts_view.xml b/addons/contacts/contacts_view.xml
new file mode 100644
index 00000000000..4908653aa2b
--- /dev/null
+++ b/addons/contacts/contacts_view.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ Contacts
+ ir.actions.act_window
+ res.partner
+ form
+ kanban,tree,form
+
+
+
+ Click to add a contact in your address book.
+
+ OpenERP helps you easily track all activities related to
+ a customer; discussions, history of business opportunities,
+ documents, etc.
+
+
+
+
+
+
+ kanban
+
+
+
+
+
+ tree
+
+
+
+
+
+ form
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/contacts/static/src/img/icon.png b/addons/contacts/static/src/img/icon.png
new file mode 100644
index 00000000000..dc9f8ee3422
Binary files /dev/null and b/addons/contacts/static/src/img/icon.png differ
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index a196f3d3f5f..135f4237f1c 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -25,7 +25,7 @@
'version': '1.0',
'category': 'Customer Relationship Management',
'sequence': 2,
- 'summary': 'Leads, Opportunities, Agenda',
+ 'summary': 'Leads, Opportunities, Phone Calls',
'description': """
The generic OpenERP Customer Relationship Management
=====================================================
diff --git a/addons/fetchmail/i18n/mk.po b/addons/fetchmail/i18n/mk.po
new file mode 100644
index 00000000000..741b3547812
--- /dev/null
+++ b/addons/fetchmail/i18n/mk.po
@@ -0,0 +1,333 @@
+# Macedonian translation for openobject-addons
+# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
+# This file is distributed under the same license as the openobject-addons package.
+# FIRST AUTHOR , 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2012-02-08 00:36+0000\n"
+"PO-Revision-Date: 2012-09-18 16:07+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Macedonian \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2012-09-19 04:37+0000\n"
+"X-Generator: Launchpad (build 15966)\n"
+
+#. module: fetchmail
+#: selection:fetchmail.server,state:0
+msgid "Confirmed"
+msgstr "Потврдено"
+
+#. module: fetchmail
+#: field:fetchmail.server,server:0
+msgid "Server Name"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,script:0
+msgid "Script"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,priority:0
+msgid "Defines the order of processing, lower values mean higher priority"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,is_ssl:0
+msgid ""
+"Connections are encrypted with SSL/TLS through a dedicated port (default: "
+"IMAPS=993, POP3S=995)"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,attach:0
+msgid "Keep Attachments"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,original:0
+msgid ""
+"Whether a full original copy of each email should be kept for referenceand "
+"attached to each processed message. This will usually double the size of "
+"your message database."
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,priority:0
+msgid "Server Priority"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,state:0
+msgid "State"
+msgstr "Состојба"
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "POP"
+msgstr "POP"
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Fetch Now"
+msgstr ""
+
+#. module: fetchmail
+#: model:ir.actions.act_window,name:fetchmail.action_email_server_tree
+#: model:ir.ui.menu,name:fetchmail.menu_action_fetchmail_server_tree
+msgid "Incoming Mail Servers"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,port:0
+msgid "Port"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "POP/IMAP Servers"
+msgstr "POP/IMAP сервери"
+
+#. module: fetchmail
+#: selection:fetchmail.server,type:0
+msgid "Local Server"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,user:0
+msgid "Username"
+msgstr ""
+
+#. module: fetchmail
+#: model:ir.model,name:fetchmail.model_fetchmail_server
+msgid "POP/IMAP Server"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Reset Confirmation"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "SSL"
+msgstr ""
+
+#. module: fetchmail
+#: model:ir.model,name:fetchmail.model_mail_message
+msgid "Email Message"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,date:0
+msgid "Last Fetch Date"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,action_id:0
+msgid ""
+"Optional custom server action to trigger for each incoming mail, on the "
+"record that was created or updated by this mail"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "# of emails"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,original:0
+msgid "Keep Original"
+msgstr ""
+
+#. module: fetchmail
+#: code:addons/fetchmail/fetchmail.py:155
+#, python-format
+msgid ""
+"Here is what we got instead:\n"
+" %s"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+#: field:fetchmail.server,configuration:0
+msgid "Configuration"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Incoming Mail Server"
+msgstr ""
+
+#. module: fetchmail
+#: code:addons/fetchmail/fetchmail.py:155
+#, python-format
+msgid "Connection test failed!"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,server:0
+msgid "Hostname or IP of the mail server"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Server type IMAP."
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,name:0
+msgid "Name"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,is_ssl:0
+msgid "SSL/TLS"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Test & Confirm"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,action_id:0
+msgid "Server Action"
+msgstr ""
+
+#. module: fetchmail
+#: field:mail.message,fetchmail_server_id:0
+msgid "Inbound Mail Server"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,message_ids:0
+#: model:ir.actions.act_window,name:fetchmail.act_server_history
+msgid "Messages"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Search Incoming Mail Servers"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,active:0
+msgid "Active"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,attach:0
+msgid ""
+"Whether attachments should be downloaded. If not enabled, incoming emails "
+"will be stripped of any attachments before being processed"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Advanced Options"
+msgstr ""
+
+#. module: fetchmail
+#: selection:fetchmail.server,type:0
+msgid "IMAP Server"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "IMAP"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Server type POP."
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,password:0
+msgid "Password"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Actions to Perform on Incoming Mails"
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,type:0
+msgid "Server Type"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Login Information"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Server Information"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "If SSL required."
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Advanced"
+msgstr ""
+
+#. module: fetchmail
+#: view:fetchmail.server:0
+msgid "Server & Login"
+msgstr ""
+
+#. module: fetchmail
+#: help:fetchmail.server,object_id:0
+msgid ""
+"Process each incoming mail as part of a conversation corresponding to this "
+"document type. This will create new documents for new conversations, or "
+"attach follow-up emails to the existing conversations (documents)."
+msgstr ""
+
+#. module: fetchmail
+#: field:fetchmail.server,object_id:0
+msgid "Create a New Record"
+msgstr ""
+
+#. module: fetchmail
+#: selection:fetchmail.server,state:0
+msgid "Not Confirmed"
+msgstr "Непотврдено"
+
+#. module: fetchmail
+#: selection:fetchmail.server,type:0
+msgid "POP Server"
+msgstr ""
+
+#. module: fetchmail
+#: view:mail.message:0
+msgid "Mail Server"
+msgstr ""
+
+#~ msgid "Confirm"
+#~ msgstr "Потврди"
+
+#~ msgid "Group By..."
+#~ msgstr "Групирај по..."
+
+#~ msgid "Type"
+#~ msgstr "Вид"
+
+#~ msgid ""
+#~ "Warning! Record for selected Model can not be created\n"
+#~ "Please choose valid Model"
+#~ msgstr ""
+#~ "Предупредување! Записот за избраниот Модел не може да се креира\n"
+#~ "Изберете валиден Модел"
diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml
index 098a8aec210..08e5f31a2d2 100644
--- a/addons/hr_recruitment/hr_recruitment_view.xml
+++ b/addons/hr_recruitment/hr_recruitment_view.xml
@@ -314,9 +314,7 @@
diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml
index 34efa05f812..b4b1aade755 100644
--- a/addons/mail/mail_thread_view.xml
+++ b/addons/mail/mail_thread_view.xml
@@ -9,7 +9,8 @@
sequence="10"/>
-
+
+
My Feeds
diff --git a/addons/note/__openerp__.py b/addons/note/__openerp__.py
index 7d6cac5b85b..2c6134e5e3a 100644
--- a/addons/note/__openerp__.py
+++ b/addons/note/__openerp__.py
@@ -20,7 +20,7 @@
##############################################################################
{
- 'name': 'Memos',
+ 'name': 'Notes',
'version': '1.0',
'category': 'Tools',
'description': """
@@ -28,12 +28,12 @@ This module allows users to create their own notes inside OpenERP
=================================================================
Use notes to write meeting minutes, organize ideas, organize personnal todo
-lists, etc. Each user manages his own personnal memos. Notes are available to
+lists, etc. Each user manages his own personnal Notes. Notes are available to
their authors only, but they can share notes to others users so that several
people can work on the same note in real time. It's very efficient to share
meeting minutes.
-Memos can be found in the 'Home' menu.
+Notes can be found in the 'Home' menu.
""",
'author': 'OpenERP SA',
'website': 'http://openerp.com',
diff --git a/addons/note/note.py b/addons/note/note.py
index 65848ed7828..fb3f0cee853 100644
--- a/addons/note/note.py
+++ b/addons/note/note.py
@@ -22,6 +22,7 @@
from openerp.osv import osv, fields
from tools.translate import _
import re
+from openerp.tools.misc import html2plaintext
class note_stage(osv.osv):
""" Category of Note """
@@ -62,26 +63,24 @@ class note_note(osv.osv):
def _get_note_first_line(self, cr, uid, ids, name="", args={}, context=None):
res = {}
for note in self.browse(cr, uid, ids, context=context):
- text_note = (note.memo or '').strip().split('\n')[0]
- text_note = re.sub(r'(\S?)( |<[/]?p>|<[/]?div>|
)[\s\S]*',r'\1',text_note)
- text_note = re.sub(r'<[^>]+>','',text_note)
- res[note.id] = text_note
+ res[note.id] = (note.memo and html2plaintext(note.memo) or "").strip().replace('*','').split("\n")[0]
+
return res
#unactivate a sticky note and record the date
def onclick_note_is_done(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, { 'active' : False, 'date_done' : fields.date.today() })
+ self.write(cr, uid, ids, { 'open' : False, 'date_done' : fields.date.today() })
self.message_post(cr, uid, ids[0], body='Note is done.', subject=False,
type='notification', parent_id=False, attachments=None, context=context)
return False
#activate a note
def onclick_note_not_done(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, {'active' : True})
+ self.write(cr, uid, ids, {'open' : True})
self.message_post(cr, uid, ids[0], body='Note has been activated.', subject=False,
type='notification', parent_id=False, attachments=None, context=context)
return False
-
+
#used for undisplay the follower if it's the current user
def _get_my_current_partner(self, cr, uid, ids, name, args, context=None):
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
@@ -96,7 +95,8 @@ class note_note(osv.osv):
def _set_stage_per_user(self, cr, uid, id, name, value, args=None, context=None):
note = self.browse(cr, uid, id, context=context)
if not value: return False
- return self.write(cr, uid, [id], {'stage_ids': [(4, value)]}, context=context)
+ stage_ids = [value] + [stage.id for stage in note.stage_ids if stage.user_id.id != uid ]
+ return self.write(cr, uid, [id], {'stage_ids': [(6, 0, stage_ids)]}, context=context)
def _get_stage_per_user(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
@@ -119,14 +119,14 @@ class note_note(osv.osv):
type='many2one',
relation='note.stage'),
'stage_ids': fields.many2many('note.stage','note_stage_rel','note_id','stage_id','Stages of Users'),
- 'active': fields.boolean('Active'),
+ 'open': fields.boolean('Active'),
'date_done': fields.date('Date done'),
'color': fields.integer('Color Index'),
'tag_ids' : fields.many2many('note.tag','note_tags_rel','note_id','tag_id','Tags'),
'current_partner_id' : fields.function(_get_my_current_partner),
}
_defaults = {
- 'active' : 1,
+ 'open' : 1,
'stage_id' : _get_default_stage_id,
}
_order = 'sequence'
diff --git a/addons/note/note_demo.xml b/addons/note/note_demo.xml
index 82601f903c0..16fa5a3f66b 100644
--- a/addons/note/note_demo.xml
+++ b/addons/note/note_demo.xml
@@ -28,7 +28,7 @@
Customer report #349872
- Customer report #349872
* Calendar app in Home
* The base_calendar module should create a menu in Home, like described above.
* This module should become a main application (in the first screen at installation)
@@ -40,7 +40,7 @@
- Call Fabien
* Followed by the telephone conversation and mail about D.544.3
]]>
@@ -48,21 +48,21 @@
- Call Marc
]]>
- Project N.947.5
]]>
- Shop for family dinner
* stuffed turkey
* wine
]]>
@@ -71,7 +71,7 @@
- Idea to develop
* Create a module note_pad
it transforms the html editable memo text field into widget='pad', similar to project_pad depends on 'memo' and 'pad' modules
]]>
@@ -80,7 +80,7 @@
- Read some documentation about OpenERP before diving into the code
* Open ERP: a modern approach to integrated business management
* Open ERP for Retail and Industrial Management
]]>
@@ -89,7 +89,7 @@
- New computer specs
* Motherboard
according to processor
* Processor
@@ -111,7 +111,7 @@
- Read those books
* Open ERP: a modern approach to integrated business management
* Open ERP for Retail and Industrial Management
]]>
@@ -120,7 +120,7 @@
- Read some documentation about OpenERP before diving into the code
* Open ERP: a modern approach to integrated business management
* Open ERP for Retail and Industrial Management
]]>
@@ -129,7 +129,7 @@
- Read some documentation about OpenERP before diving into the code
* Open ERP: a modern approach to integrated business management
* Open ERP for Retail and Industrial Management
]]>
@@ -138,14 +138,5 @@
7
-
- * Open ERP: a modern approach to integrated business management
- * Open ERP for Retail and Industrial Management
- ]]>
-
-
-
-
diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml
index 1166e687750..8d943dde44d 100644
--- a/addons/note/note_view.xml
+++ b/addons/note/note_view.xml
@@ -1,9 +1,6 @@
-
-
-
note.stage.form
@@ -39,8 +36,6 @@
[('user_id','=',uid)]
-
-
note.note.kanban
@@ -51,7 +46,7 @@
-
+
@@ -65,8 +60,8 @@
@@ -105,7 +99,7 @@
-
+
@@ -119,12 +113,10 @@
@@ -139,9 +131,8 @@
-
-
-
+
+
@@ -185,10 +176,11 @@
formkanban,tree,form
- {'search_default_active_true':True}
+ {'search_default_open_true':True}
-
+
+
diff --git a/addons/note/security/ir.rule.xml b/addons/note/security/ir.rule.xml
index 3d2f0d242f6..1195314cc9e 100644
--- a/addons/note/security/ir.rule.xml
+++ b/addons/note/security/ir.rule.xml
@@ -1,12 +1,19 @@
-
- Only followers can access a sticky notes
-
- ['|',('message_follower_ids','=',False),('message_follower_ids','=',user.partner_id.id)]
-
-
+
+ Only followers can access a sticky notes
+
+ [('message_follower_ids','=',user.partner_id.id)]
+
+
+
+
+ Each user have his stage name
+
+ ['|',('user_id','=',False),('user_id','=',user.id)]
+
+
diff --git a/addons/note_pad/note_pad.py b/addons/note_pad/note_pad.py
index 9864f0ae14f..d0c18a9f986 100644
--- a/addons/note_pad/note_pad.py
+++ b/addons/note_pad/note_pad.py
@@ -31,11 +31,5 @@ class note_pad_note(osv.osv):
_pad_fields = ['note_pad']
_columns = {
- 'note_pad_url': fields.char('Pad Url',
- pad_content_field='memo',
- size=250 ),
- }
-
- #_defaults = {
- # 'note_pad_url': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context),
- #}
+ 'note_pad_url': fields.char('Pad Url', pad_content_field='memo'),
+ }
\ No newline at end of file
diff --git a/addons/pad/pad.py b/addons/pad/pad.py
index d21ccd42d16..48ac940ccfa 100644
--- a/addons/pad/pad.py
+++ b/addons/pad/pad.py
@@ -21,7 +21,7 @@ class pad_common(osv.osv_memory):
s = string.ascii_uppercase + string.digits
salt = ''.join([s[random.randint(0, len(s) - 1)] for i in range(10)])
# contruct the url
- url = '%s/p/%s-%s-%s' % (pad_server, cr.dbname, self._name, salt)
+ url = '%s/p/%s-%s-%s' % (pad_server, cr.dbname.replace('_','-'), self._name, salt)
return url
def pad_get_content(self, cr, uid, url, context=None):
@@ -35,22 +35,21 @@ class pad_common(osv.osv_memory):
# TODO
# reverse engineer protocol to be setHtml without using the api key
- # override read and copy to generate url and store the content if empty
-
- def default_get(self, cr, uid, fields, context=None):
- data = super(pad_common, self).default_get(cr, uid, fields, context)
- for k in fields:
- field = self._all_columns[k].column
- if hasattr(field,'pad_content_field'):
- data[k] = self.pad_generate_url(cr, uid, context=context)
- return data
def write(self, cr, uid, ids, vals, context=None):
+ self._set_pad_value(cr, uid, vals, context)
+ return super(pad_common, self).write(cr, uid, ids, vals, context=context)
+
+ def create(self, cr, uid, vals, context=None):
+ self._set_pad_value(cr, uid, vals, context)
+ return super(pad_common, self).create(cr, uid, vals, context=context)
+
+ # Set the pad content in vals
+ def _set_pad_value(self, cr, uid, vals, context=None):
for k,v in vals.items():
field = self._all_columns[k].column
if hasattr(field,'pad_content_field'):
- vals[field.pad_content_field] = self.pad_get_content(cr, uid, v, context=context)
- return super(pad_common, self).write(cr, uid, ids, vals, context=context)
+ vals[field.pad_content_field] = self.pad_get_content(cr, uid, v, context=context)
def copy(self, cr, uid, id, default=None, context=None):
if not default:
diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js
index 511400bcede..b7c13ba1b89 100644
--- a/addons/pad/static/src/js/pad.js
+++ b/addons/pad/static/src/js/pad.js
@@ -16,8 +16,15 @@ instance.web.form.FieldPad = instance.web.form.AbstractField.extend({
var self = this;
var _super = self._super;
_super.apply(self,[val]);
+ if (val === false || val === "") {
+ self.field_manager.dataset.call('pad_generate_url').then(function(r) {
+ _super.apply(self,[r]);
+ self.render_value();
+ });
+ } else {
+ self.render_value();
+ }
this._dirty_flag = true;
- self.render_value();
},
render_value: function() {
console.log("display");
diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py
index 72c8469c042..a41c28f15e2 100644
--- a/addons/portal/mail_mail.py
+++ b/addons/portal/mail_mail.py
@@ -43,7 +43,7 @@ class mail_mail_portal(osv.Model):
:param partner: browse_record of the specific recipient partner
"""
if partner:
- portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal')
+ portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
portal_id = portal_ref and portal_ref[1] or False
url = self._generate_signin_url(cr, uid, partner.id, portal_id, 1234, context=context)
body = tools.append_content_to_html(mail.body_html, url)
diff --git a/addons/portal/portal.py b/addons/portal/portal.py
index 3672d87cf30..13cde80f849 100644
--- a/addons/portal/portal.py
+++ b/addons/portal/portal.py
@@ -20,196 +20,14 @@
##############################################################################
from osv import osv, fields
-from tools.translate import _
-
class portal(osv.osv):
+ """ A portal is simply a group of users with the flag 'is_portal' set to True.
+ The flag 'is_portal' makes a user group usable as a portal.
"""
- A portal is a group of users with specific menu, widgets, and typically
- restricted access rights.
- """
- _name = 'res.portal'
- _description = 'Portal'
- _inherits = {'res.groups': 'group_id'}
-
+ _inherit = 'res.groups'
_columns = {
- 'group_id': fields.many2one('res.groups', required=True, ondelete='cascade',
- string='Group',
- help='The group corresponding to this portal'),
- 'url': fields.char('URL',
- help="The url where portal users can connect to the server"),
- 'home_action_id': fields.many2one('ir.actions.actions',
- string='Home Action',
- help="if set, replaces the standard home action (first screen after loggin) for the portal's users"),
- 'menu_action_id': fields.many2one('ir.actions.act_window', readonly=True,
- # ISSUE: 'ondelete' constraints do not seem effective on this field...
- string='Menu Action',
- help="If set, replaces the standard menu for the portal's users"),
- 'parent_menu_id': fields.many2one('ir.ui.menu', ondelete='restrict',
- string='Parent Menu',
- help='The menu action opens the submenus of this menu item'),
- 'widget_ids': fields.one2many('res.portal.widget', 'portal_id',
- string='Widgets',
- help='Widgets assigned to portal users'),
+ 'is_portal': fields.boolean('Portal', help="If checked, this group is usable as a portal."),
}
- def copy(self, cr, uid, id, values, context=None):
- """ override copy(): menu_action_id must be different """
- values['menu_action_id'] = None
- return super(portal, self).copy(cr, uid, id, values, context)
-
- def create(self, cr, uid, values, context=None):
- """ extend create() to assign the portal menu to users """
- if context is None:
- context = {}
-
- # create portal (admin should not be included)
- context['noadmin'] = True
- portal_id = super(portal, self).create(cr, uid, values, context)
-
- # assign menu action and widgets to users
- if values.get('users') or values.get('menu_action_id'):
- self._assign_menu(cr, uid, [portal_id], context)
- if values.get('users') or values.get('widget_ids'):
- self._assign_widgets(cr, uid, [portal_id], context)
-
- return portal_id
-
- def write(self, cr, uid, ids, values, context=None):
- """ extend write() to reflect changes on users """
- # first apply portal changes
- super(portal, self).write(cr, uid, ids, values, context)
-
- # assign menu action and widgets to users
- if values.get('users') or values.get('menu_action_id'):
- self._assign_menu(cr, uid, ids, context)
- if values.get('users') or values.get('widget_ids'):
- self._assign_widgets(cr, uid, ids, context)
-
- # if parent_menu_id has changed, apply the change on menu_action_id
- if 'parent_menu_id' in values:
- act_window_obj = self.pool.get('ir.actions.act_window')
- portals = self.browse(cr, uid, ids, context)
- action_ids = [p.menu_action_id.id for p in portals if p.menu_action_id]
- if action_ids:
- action_values = {'domain': [('parent_id', '=', values['parent_menu_id'])]}
- act_window_obj.write(cr, uid, action_ids, action_values, context)
-
- return True
-
- def _assign_menu(self, cr, uid, ids, context=None):
- """ assign portal_menu_settings to users of portals (ids) """
- user_obj = self.pool.get('res.users')
- for p in self.browse(cr, uid, ids, context):
- # user menu action = portal menu action if set in portal
- if p.menu_action_id:
- user_ids = [u.id for u in p.users if u.id != 1]
- user_values = {'menu_id': p.menu_action_id.id}
- user_obj.write(cr, uid, user_ids, user_values, context)
-
- def _assign_widgets(self, cr, uid, ids, context=None):
- """ assign portal widgets to users of portals (ids) """
- widget_user_obj = self.pool.get('res.widget.user')
- for p in self.browse(cr, uid, ids, context):
- for w in p.widget_ids:
- values = {'sequence': w.sequence, 'widget_id': w.widget_id.id}
- for u in p.users:
- if u.id == 1: continue
- values['user_id'] = u.id
- widget_user_obj.create(cr, uid, values, context)
-
- def _res_xml_id(self, cr, uid, module, xml_id):
- """ return the resource id associated to the given xml_id """
- data_obj = self.pool.get('ir.model.data')
- data_id = data_obj._get_id(cr, uid, module, xml_id)
- return data_obj.browse(cr, uid, data_id).res_id
-
-portal()
-
-
-
-class portal_override_menu(osv.osv):
- """
- Extend res.portal with a boolean field 'Override Users Menu', that
- triggers the creation or removal of menu_action_id
- """
- _name = 'res.portal'
- _inherit = 'res.portal'
-
- def _get_override_menu(self, cr, uid, ids, field_name, arg, context=None):
- assert field_name == 'override_menu'
- result = {}
- for p in self.browse(cr, uid, ids, context):
- result[p.id] = bool(p.menu_action_id)
- return result
-
- def _set_override_menu(self, cr, uid, id, field_name, field_value, arg, context=None):
- assert field_name == 'override_menu'
- if field_value:
- self.create_menu_action(cr, uid, id, context)
- else:
- self.write(cr, uid, [id], {'menu_action_id': False}, context)
-
- def create_menu_action(self, cr, uid, id, context=None):
- """ create, if necessary, a menu action that opens the menu items below
- parent_menu_id """
- p = self.browse(cr, uid, id, context)
- if not p.menu_action_id:
- actions_obj = self.pool.get('ir.actions.act_window')
- parent_id = p.parent_menu_id.id if p.parent_menu_id else False
- action_values = {
- 'name': _('%s Menu') % p.name,
- 'type': 'ir.actions.act_window',
- 'usage': 'menu',
- 'res_model': 'ir.ui.menu',
- 'view_type': 'tree',
- 'view_id': self._res_xml_id(cr, uid, 'base', 'view_menu'),
- 'domain': [('parent_id', '=', parent_id)],
- }
- action_id = actions_obj.create(cr, uid, action_values, context)
- self.write(cr, uid, [id], {'menu_action_id': action_id}, context)
-
- _columns = {
- 'override_menu': fields.function(
- _get_override_menu, fnct_inv=_set_override_menu,
- type='boolean', string='Override Menu Action of Users',
- help='Enable this option to override the Menu Action of portal users'),
- }
-
-portal_override_menu()
-
-
-
-class portal_widget(osv.osv):
- """
- Similar to res.widget.user (res_widget.py), but with a portal instead.
- New users in a portal are assigned the portal's widgets.
- """
- _name='res.portal.widget'
- _description = 'Portal Widgets'
- _order = 'sequence'
- _columns = {
- 'sequence': fields.integer('Sequence'),
- 'portal_id': fields.many2one('res.portal', select=1, ondelete='cascade',
- string='Portal'),
- 'widget_id': fields.many2one('res.widget', required=True, ondelete='cascade',
- string='Widget'),
- }
-
- def create(self, cr, uid, values, context=None):
- domain = [('portal_id', '=', values.get('portal_id')),
- ('widget_id', '=', values.get('widget_id'))]
- existing = self.search(cr, uid, domain, context=context)
- if existing:
- res = existing[0]
- else:
- res = super(portal_widget, self).create(cr, uid, values, context=context)
- return res
-
-portal_widget()
-
-
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/portal/portal_data.xml b/addons/portal/portal_data.xml
index eb06ca656d6..a980552a854 100644
--- a/addons/portal/portal_data.xml
+++ b/addons/portal/portal_data.xml
@@ -2,14 +2,6 @@
-
- Portal
-
-
-
-
-
-
Company's news
diff --git a/addons/portal/portal_demo.xml b/addons/portal/portal_demo.xml
index 1ace02f3d7d..f4ba8fe2192 100644
--- a/addons/portal/portal_demo.xml
+++ b/addons/portal/portal_demo.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/addons/portal/portal_view.xml b/addons/portal/portal_view.xml
index fd15a842405..16e8a86b27e 100644
--- a/addons/portal/portal_view.xml
+++ b/addons/portal/portal_view.xml
@@ -5,7 +5,7 @@
@@ -21,71 +21,31 @@
-
-
- Portal List
- res.portal
+
+
+ Group Search
+ res.groups
+
-
-
-
+
+
+
+
+
-
-
+
+ Portal Form
- res.portal
+ res.groups
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
- Portals
- res.portal
- form
- tree,form
-
- {'form_view_ref': 'portal.portal_form_view'}
-
-
- Click to create a new portal.
-
- A portal helps defining specific views and rules for a group of
- users (the portal group). A portal menu, widgets and specific
- groups may be assigned to the portal's users.
-
-
-
-
-
-
-
-
diff --git a/addons/portal/security/ir.model.access.csv b/addons/portal/security/ir.model.access.csv
index 56b3ae5f269..2d7a17e0016 100644
--- a/addons/portal/security/ir.model.access.csv
+++ b/addons/portal/security/ir.model.access.csv
@@ -1,12 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_all,access.portal.all,model_res_portal,,1,0,0,0
-access_widget_all,access.portal.widget.all,model_res_portal_widget,,1,0,0,0
-access_manager,access.portal.manager,model_res_portal,group_portal_manager,1,1,1,1
-access_widget_manager,access.portal.widget.manager,model_res_portal_widget,group_portal_manager,1,1,1,1
-access_mail_message,mail.message,mail.model_mail_message,group_portal_member,1,0,1,1
-access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal_member,1,0,0,0
-access_mail_thread,mail.thread,mail.model_mail_thread,group_portal_member,1,0,0,0
-access_mail_followers,mail.followers,mail.model_mail_followers,group_portal_member,1,0,1,1
-access_mail_notification,mail.notification,mail.model_mail_notification,group_portal_member,1,0,1,0
-access_mail_group,mail.group,mail.model_mail_group,group_portal_member,1,0,0,0
-access_mail_alias,mail.alias,mail.model_mail_alias,group_portal_member,1,0,0,0
+access_mail_message,mail.message,mail.model_mail_message,group_portal,1,0,1,1
+access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal,1,0,0,0
+access_mail_thread,mail.thread,mail.model_mail_thread,group_portal,1,0,0,0
+access_mail_followers,mail.followers,mail.model_mail_followers,group_portal,1,0,1,1
+access_mail_notification,mail.notification,mail.model_mail_notification,group_portal,1,0,1,0
+access_mail_group,mail.group,mail.model_mail_group,group_portal,1,0,0,0
+access_mail_alias,mail.alias,mail.model_mail_alias,group_portal,1,0,0,0
diff --git a/addons/portal/security/portal_security.xml b/addons/portal/security/portal_security.xml
index 4a493204d67..ee0af785599 100644
--- a/addons/portal/security/portal_security.xml
+++ b/addons/portal/security/portal_security.xml
@@ -2,31 +2,14 @@
-
-
- Portal Member
- Portal members can access information through the portal menu. Also, they are chrooted in this specific menu.
-
-
-
+
+ Portal
-
- 25
-
-
-
-
- Officer
- Portal officers can create new portal users with the portal wizard.
-
-
-
-
- Manager
- Portal managers have access to the portal definitions, and can easily configure the users, access rights and menus of portal users.
-
-
-
+ Portal members have specific access rights (such as record rules and restricted menus).
+ They usually do not belong to the usual OpenERP groups.
+
+
+
diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py
index 2c301228859..5e992fa06ea 100644
--- a/addons/portal/tests/test_portal.py
+++ b/addons/portal/tests/test_portal.py
@@ -42,7 +42,7 @@ class test_portal(test_mail.TestMailMockups):
user_admin = self.res_users.browse(cr, uid, uid)
self.mail_invite = self.registry('mail.wizard.invite')
base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='')
- portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal')
+ portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
portal_id = portal_ref and portal_ref[1] or False
# 0 - Admin
diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py
index 85f6566eba6..3cc706c2129 100644
--- a/addons/portal/wizard/portal_wizard.py
+++ b/addons/portal/wizard/portal_wizard.py
@@ -23,33 +23,43 @@ import logging
import random
from osv import osv, fields
-from tools.misc import email_re
from tools.translate import _
+from tools.misc import email_re
+from openerp import SUPERUSER_ID
from base.res.res_partner import _lang_get
_logger = logging.getLogger(__name__)
-
-# welcome email sent to new portal users (note that calling tools.translate._
-# has no effect except exporting those strings for translation)
+# welcome/goodbye email sent to portal users
+# (note that calling '_' has no effect except exporting those strings for translation)
WELCOME_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
WELCOME_EMAIL_BODY = _("""Dear %(name)s,
-You have been created an OpenERP account at %(url)s.
+You have been given access to %(portal)s at %(url)s.
Your login account data is:
Database: %(db)s
User: %(login)s
Password: %(password)s
-%(message)s
+%(welcome_message)s
--
OpenERP - Open Source Business Applications
http://www.openerp.com
""")
-ROOT_UID = 1
+GOODBYE_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
+GOODBYE_EMAIL_BODY = _("""Dear %(name)s,
+
+Your access to %(portal)s has been withdrawn.
+
+%(goodbye_message)s
+
+--
+OpenERP - Open Source Business Applications
+http://www.openerp.com
+""")
# character sets for passwords, excluding 0, O, o, 1, I, l
_PASSU = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'
@@ -71,169 +81,173 @@ def extract_email(email):
class wizard(osv.osv_memory):
"""
- A wizard to create portal users from instances of 'res.partner'. The purpose
- is to provide an OpenERP database access to customers or suppliers.
+ A wizard to manage the creation/removal of portal users.
"""
- _name = 'res.portal.wizard'
- _description = 'Portal Wizard'
-
+ _name = 'portal.wizard'
+ _description = 'Portal Access Management'
+
_columns = {
- 'portal_id': fields.many2one('res.portal', required=True,
- string='Portal',
- help="The portal in which new users must be added"),
- 'user_ids': fields.one2many('res.portal.wizard.user', 'wizard_id',
- string='Users'),
- 'message': fields.text(string='Invitation message',
- help="This text is included in the welcome email sent to the users"),
+ 'portal_id': fields.many2one('res.groups', domain=[('is_portal', '=', True)], required=True,
+ string='Portal', help="The portal that users can be added in or removed from."),
+ 'user_ids': fields.one2many('portal.wizard.user', 'wizard_id', string='Users'),
+ 'welcome_message': fields.text(string='Invitation Message',
+ help="This text is included in the email sent to new users of the portal."),
+ 'goodbye_message': fields.text(string='Withdrawal Message',
+ help="This text is included in the email sent to users withdrawn from the portal."),
}
- def _default_user_ids(self, cr, uid, context):
- """ determine default user_ids from the active records """
- def create_user_from_address(address):
- if isinstance(address, int):
- res_partner_obj = self.pool.get('res.partner')
- address = res_partner_obj.browse(cr, uid, address, context=context)
- lang = address.id and address.lang or 'en_US'
- partner_id = address.id
-
- else:
- lang = address.parent_id and address.parent_id.lang or 'en_US'
- partner_id = address.parent_id and address.parent_id.id
-
- return{
- 'name': address.name,
- 'email': extract_email(address.email),
- 'lang': lang,
- 'partner_id': partner_id,
- }
-
- user_ids = []
- if context.get('active_model') == 'res.partner':
- partner_obj = self.pool.get('res.partner')
- partner_ids = context.get('active_ids', [])
- partners = partner_obj.browse(cr, uid, partner_ids, context)
- for p in partners:
- # add one user per contact, or one user if no contact
- if p.child_ids:
- user_ids.extend(map(create_user_from_address, p.child_ids))
- elif p.is_company == False and p.customer == True:
- user_ids.extend(map(create_user_from_address, [p.id]))
- else:
- user_ids.append({'lang': p.lang or 'en_US', 'parent_id': p.id})
-
- return user_ids
+ def _default_portal(self, cr, uid, context):
+ portal_ids = self.pool.get('res.groups').search(cr, uid, [('is_portal', '=', True)])
+ return portal_ids and portal_ids[0] or False
_defaults = {
- 'user_ids': _default_user_ids
+ 'portal_id': _default_portal,
}
- def action_create(self, cr, uid, ids, context=None):
- """ create new users in portal(s), and notify them by email """
- # we copy the context to change the language for translating emails
- context0 = context or {}
- context0['noshortcut'] = True # prevent shortcut creation
- context = context0.copy()
-
- user_obj = self.pool.get('res.users')
- user = user_obj.browse(cr, ROOT_UID, uid, context0)
- if not user.email:
- raise osv.except_osv(_('Email required'),
- _('You must have an email address in your User Preferences'
- ' to send emails.'))
-
- portal_obj = self.pool.get('res.portal')
- for wiz in self.browse(cr, uid, ids, context):
- # determine existing users
- login_cond = [('login', 'in', [u.email for u in wiz.user_ids])]
- existing_uids = user_obj.search(cr, ROOT_UID, login_cond)
- existing_users = user_obj.browse(cr, ROOT_UID, existing_uids)
- existing_logins = [u.login for u in existing_users]
-
- # create new users in portal (skip existing logins)
- new_users_data = [ {
- 'name': u.name,
- 'login': u.email,
- 'password': random_password(),
- 'email': u.email,
- 'lang': u.lang,
- 'share': True,
- 'action_id': wiz.portal_id.home_action_id and wiz.portal_id.home_action_id.id or False,
- 'partner_id': u.partner_id and u.partner_id.id,
- 'groups_id': [(6, 0, [])],
- } for u in wiz.user_ids if u.email not in existing_logins ]
- portal_obj.write(cr, ROOT_UID, [wiz.portal_id.id],
- {'users': [(0, 0, data) for data in new_users_data]}, context0)
-
- # send email to all users (translated in their language)
- data = {
- 'company': user.company_id.name,
- 'message': wiz.message or "",
- 'url': wiz.portal_id.url or _("(missing url)"),
- 'db': cr.dbname,
- }
- mail_mail_obj = self.pool.get('mail.mail')
- dest_uids = user_obj.search(cr, ROOT_UID, login_cond)
- dest_users = user_obj.browse(cr, ROOT_UID, dest_uids)
- for dest_user in dest_users:
- context['lang'] = dest_user.lang
- data['login'] = dest_user.login
- data['password'] = dest_user.password
- data['name'] = dest_user.name
-
- email_from = user.email
- email_to = dest_user.email
- subject = _(WELCOME_EMAIL_SUBJECT) % data
- body = _(WELCOME_EMAIL_BODY) % data
- mail_id = mail_mail_obj.create(cr, uid, {
- 'email_from': email_from ,
- 'email_to': email_to,
- 'subject': subject,
- 'state': 'outgoing',
- 'body_html': '
%s
' % body}, context=context)
-
+ def onchange_portal_id(self, cr, uid, ids, portal_id, context=None):
+ # for each partner, determine corresponding portal.wizard.user records
+ res_partner = self.pool.get('res.partner')
+ partner_ids = context and context.get('active_ids') or []
+ contact_ids = set()
+ user_changes = []
+ for partner in res_partner.browse(cr, SUPERUSER_ID, partner_ids, context):
+ for contact in (partner.child_ids or [partner]):
+ # make sure that each contact appears at most once in the list
+ if contact.id not in contact_ids:
+ contact_ids.add(contact.id)
+ in_portal = False
+ if contact.user_ids:
+ in_portal = portal_id in [g.id for g in contact.user_ids[0].groups_id]
+ user_changes.append((0, 0, {
+ 'partner_id': contact.id,
+ 'email': contact.email,
+ 'in_portal': in_portal,
+ }))
+ return {'value': {'user_ids': user_changes}}
+
+ def action_apply(self, cr, uid, ids, context=None):
+ wizard = self.browse(cr, uid, ids[0], context)
+ portal_user_ids = [user.id for user in wizard.user_ids]
+ self.pool.get('portal.wizard.user').action_apply(cr, uid, portal_user_ids, context)
return {'type': 'ir.actions.act_window_close'}
-wizard()
-
-
-
class wizard_user(osv.osv_memory):
"""
A model to configure users in the portal wizard.
"""
- _name = 'res.portal.wizard.user'
+ _name = 'portal.wizard.user'
_description = 'Portal User Config'
_columns = {
- 'wizard_id': fields.many2one('res.portal.wizard', required=True,
- string='Wizard'),
- 'name': fields.char(size=64, required=True,
- string='User Name',
- help="The user's real name"),
- 'email': fields.char(size=64, required=True,
- string='Email',
- help="Will be used as user login. "
- "Also necessary to send the account information to new users"),
- 'lang': fields.selection(_lang_get, required=True,
- string='Language',
- help="The language for the user's user interface"),
- 'partner_id': fields.many2one('res.partner',
- string='Partner'),
+ 'wizard_id': fields.many2one('portal.wizard', string='Wizard', required=True),
+ 'partner_id': fields.many2one('res.partner', string='Contact', required=True, readonly=True),
+ 'email': fields.char(size=240, string='Email'),
+ 'in_portal': fields.boolean('In Portal'),
}
- def _check_email(self, cr, uid, ids):
- """ check syntax of email address """
- for wuser in self.browse(cr, uid, ids):
- if not email_re.match(wuser.email): return False
- return True
+ def create(self, cr, uid, values, context=None):
+ """ overridden to update the partner's email (if necessary) """
+ id = super(wizard_user, self).create(cr, uid, values, context)
+ wuser = self.browse(cr, uid, id, context)
+ if wuser.partner_id.email != wuser.email:
+ wuser.partner_id.write({'email': wuser.email})
+ return id
- _constraints = [
- (_check_email, 'Invalid email address', ['email']),
- ]
+ def action_apply(self, cr, uid, ids, context=None):
+ res_users = self.pool.get('res.users')
+ for wizard_user in self.browse(cr, SUPERUSER_ID, ids, context):
+ portal = wizard_user.wizard_id.portal_id
+ user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context)
+ if wizard_user.in_portal:
+ # create a user if necessary, and make sure it is in the portal group
+ if not user:
+ user = self._create_user(cr, SUPERUSER_ID, wizard_user, context)
+ if (not user.active) or (portal not in user.groups_id):
+ user.write({'active': True, 'groups_id': [(4, portal.id)]})
+ wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context)
+ self._send_email(cr, uid, wizard_user, context)
+ else:
+ # remove the user (if it exists) from the portal group
+ if user and (portal in user.groups_id):
+ # if user belongs to portal only, deactivate it
+ if len(user.groups_id) <= 1:
+ user.write({'groups_id': [(3, portal.id)], 'active': False})
+ else:
+ user.write({'groups_id': [(3, portal.id)]})
+ wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context)
+ self._send_email(cr, uid, wizard_user, context)
-wizard_user()
+ def _retrieve_user(self, cr, uid, wizard_user, context=None):
+ """ retrieve the (possibly inactive) user corresponding to wizard_user.partner_id
+ @param wizard_user: browse record of model portal.wizard.user
+ @return: browse record of model res.users
+ """
+ if wizard_user.partner_id.user_ids:
+ return wizard_user.partner_id.user_ids[0]
+ # the user may be inactive, search for it
+ res_users = self.pool.get('res.users')
+ domain = [('partner_id', '=', wizard_user.partner_id.id), ('active', '=', False)]
+ user_ids = res_users.search(cr, uid, domain)
+ return user_ids and res_users.browse(cr, uid, user_ids[0], context) or False
+ def _create_user(self, cr, uid, wizard_user, context=None):
+ """ create a new user for wizard_user.partner_id
+ @param wizard_user: browse record of model portal.wizard.user
+ @return: browse record of model res.users
+ """
+ res_users = self.pool.get('res.users')
+ create_context = dict(context or {}, noshortcut=True) # to prevent shortcut creation
+ values = {
+ 'login': extract_email(wizard_user.email),
+ 'password': random_password(),
+ 'partner_id': wizard_user.partner_id.id,
+ 'groups_id': [(6, 0, [])],
+ 'share': True,
+ }
+ user_id = res_users.create(cr, uid, values, context=create_context)
+ return res_users.browse(cr, uid, user_id, context)
+ def _send_email(self, cr, uid, wizard_user, context=None):
+ """ send notification email to a new/former portal user
+ @param wizard_user: browse record of model portal.wizard.user
+ @return: the id of the created mail.mail record
+ """
+ this_context = context
+ this_user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context)
+ if not this_user.email:
+ raise osv.except_osv(_('Email required'),
+ _('You must have an email address in your User Preferences to send emails.'))
+ # determine subject and body in the portal user's language
+ url = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'web.base.url', context=this_context)
+ user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context)
+ context = dict(this_context or {}, lang=user.lang)
+ data = {
+ 'company': this_user.company_id.name,
+ 'portal': wizard_user.wizard_id.portal_id.name,
+ 'welcome_message': wizard_user.wizard_id.welcome_message or "",
+ 'goodbye_message': wizard_user.wizard_id.goodbye_message or "",
+ 'url': url or _("(missing url)"),
+ 'db': cr.dbname,
+ 'login': user.login,
+ 'password': user.password,
+ 'name': user.name
+ }
+ if wizard_user.in_portal:
+ subject = _(WELCOME_EMAIL_SUBJECT) % data
+ body = _(WELCOME_EMAIL_BODY) % data
+ else:
+ subject = _(GOODBYE_EMAIL_SUBJECT) % data
+ body = _(GOODBYE_EMAIL_BODY) % data
+
+ mail_mail = self.pool.get('mail.mail')
+ mail_values = {
+ 'email_from': this_user.email,
+ 'email_to': user.email,
+ 'subject': subject,
+ 'body_html': '
%s
' % body,
+ 'state': 'outgoing',
+ }
+ return mail_mail.create(cr, uid, mail_values, context=this_context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/portal/wizard/portal_wizard_view.xml b/addons/portal/wizard/portal_wizard_view.xml
index f3cd5c98bb4..c8e3f30b128 100644
--- a/addons/portal/wizard/portal_wizard_view.xml
+++ b/addons/portal/wizard/portal_wizard_view.xml
@@ -3,28 +3,34 @@
+ groups="base.group_partner_manager"/>
- Add Portal Access
- res.portal.wizard
+ Portal Access Management
+ portal.wizard
-
diff --git a/addons/portal/wizard/share_wizard.py b/addons/portal/wizard/share_wizard.py
index ca950a61b08..b1b6e980dec 100644
--- a/addons/portal/wizard/share_wizard.py
+++ b/addons/portal/wizard/share_wizard.py
@@ -44,9 +44,6 @@ class share_wizard_portal(osv.TransientModel):
'group_ids': fields.many2many('res.groups', 'share_wizard_res_group_rel', 'share_id', 'group_id', 'Existing groups', domain=[('share', '=', False)]),
}
- def is_portal_manager(self, cr, uid, context=None):
- return self.has_group(cr, uid, module='portal', group_xml_id='group_portal_manager', context=context)
-
def _check_preconditions(self, cr, uid, wizard_data, context=None):
if wizard_data.user_type == 'existing':
self._assert(wizard_data.user_ids,
@@ -122,16 +119,14 @@ class share_wizard_portal(osv.TransientModel):
# alter the rules of the groups so they can see the shared data
if wizard_data.group_ids:
# get the list of portals and the related groups to install their menus.
- Portals = self.pool.get('res.portal')
- all_portals = Portals.browse(cr, UID_ROOT, Portals.search(cr, UID_ROOT, [])) #no context!
- all_portal_group_ids = [p.group_id.id for p in all_portals]
+ res_groups = self.pool.get('res.groups')
+ all_portal_group_ids = res_groups.search(cr, UID_ROOT, [('is_portal', '=', True)])
# populate result lines with the users of each group and
# setup the menu for portal groups
for group in wizard_data.group_ids:
if group.id in all_portal_group_ids:
- portal = all_portals[all_portal_group_ids.index(group.id)]
- self._create_shared_data_menu(cr, uid, wizard_data, portal, context=context)
+ self._create_shared_data_menu(cr, uid, wizard_data, group.id, context=context)
for user in group.users:
new_line = {'user_id': user.id,
diff --git a/addons/portal/wizard/share_wizard_view.xml b/addons/portal/wizard/share_wizard_view.xml
index 72ae857eaaf..065a7aa317e 100644
--- a/addons/portal/wizard/share_wizard_view.xml
+++ b/addons/portal/wizard/share_wizard_view.xml
@@ -36,12 +36,12 @@
-
+
-
-
+
+
diff --git a/addons/portal_claim/security/ir.model.access.csv b/addons/portal_claim/security/ir.model.access.csv
index 554c66cc998..ab2a2f66770 100644
--- a/addons/portal_claim/security/ir.model.access.csv
+++ b/addons/portal_claim/security/ir.model.access.csv
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal_member,1,0,0,0
-access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal_member,1,0,0,0
+access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal,1,0,0,0
+access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal,1,0,0,0
diff --git a/addons/portal_claim/security/portal_security.xml b/addons/portal_claim/security/portal_security.xml
index e902bd043ad..9fc47c4cfb0 100644
--- a/addons/portal_claim/security/portal_security.xml
+++ b/addons/portal_claim/security/portal_security.xml
@@ -6,7 +6,7 @@
Portal Personal Claims[('partner_id','child_of',user.partner_id.id)]
-
+
diff --git a/addons/portal_event/security/ir.model.access.csv b/addons/portal_event/security/ir.model.access.csv
index b418acbb9c4..7418b08bb4b 100644
--- a/addons/portal_event/security/ir.model.access.csv
+++ b/addons/portal_event/security/ir.model.access.csv
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_event,event,event.model_event_event,portal.group_portal_member,1,0,0,0
-access_registration,registration,event.model_event_registration,portal.group_portal_member,1,0,0,0
+access_event,event,event.model_event_event,portal.group_portal,1,0,0,0
+access_registration,registration,event.model_event_registration,portal.group_portal,1,0,0,0
diff --git a/addons/portal_event/security/portal_security.xml b/addons/portal_event/security/portal_security.xml
index 6a3ed0ac786..74b2372c147 100644
--- a/addons/portal_event/security/portal_security.xml
+++ b/addons/portal_event/security/portal_security.xml
@@ -6,14 +6,14 @@
Portal Visible Events[('visibility', '=', 'public')]
-
+ Portal Personal Registrations[('partner_id','child_of',user.partner_id.id)]
-
+
diff --git a/addons/portal_hr_employees/security/ir.model.access.csv b/addons/portal_hr_employees/security/ir.model.access.csv
index 6131f49d749..d18aeb90e1c 100644
--- a/addons/portal_hr_employees/security/ir.model.access.csv
+++ b/addons/portal_hr_employees/security/ir.model.access.csv
@@ -1,6 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_hr_employee_user,hr.employee user,hr.model_hr_employee,portal.group_portal_member,1,0,0,0
-access_res_partner,res.partner,base.model_res_partner,portal.group_portal_member,1,0,0,0
-access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal_member,1,0,0,0
-access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal_member,1,0,0,0
-access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal_member,1,0,0,0
+access_hr_employee_user,hr.employee user,hr.model_hr_employee,portal.group_portal,1,0,0,0
+access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,0
+access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal,1,0,0,0
+access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal,1,0,0,0
+access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal,1,0,0,0
diff --git a/addons/portal_project/security/ir.model.access.csv b/addons/portal_project/security/ir.model.access.csv
index dceca5ba8bb..85463868ee1 100644
--- a/addons/portal_project/security/ir.model.access.csv
+++ b/addons/portal_project/security/ir.model.access.csv
@@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_task,tasks,project.model_project_task,portal.group_portal_member,1,0,0,0
-access_task_type,task_type,project.model_project_task_type,portal.group_portal_member,1,0,0,0
-access_task_work,task_work,project.model_project_task_work,portal.group_portal_member,1,0,0,0
-access_project_category,project_category,project.model_project_category,portal.group_portal_member,1,0,0,0
+access_task,tasks,project.model_project_task,portal.group_portal,1,0,0,0
+access_task_type,task_type,project.model_project_task_type,portal.group_portal,1,0,0,0
+access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0
+access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0
diff --git a/addons/portal_project/security/portal_security.xml b/addons/portal_project/security/portal_security.xml
index b995c79bbbc..b9d37cec099 100644
--- a/addons/portal_project/security/portal_security.xml
+++ b/addons/portal_project/security/portal_security.xml
@@ -6,7 +6,7 @@
Portal Personal Task[('partner_id','child_of',user.partner_id.id)]
-
+
diff --git a/addons/portal_project_issue/portal_project_issue_view.xml b/addons/portal_project_issue/portal_project_issue_view.xml
index 5d0b4ce081e..1030dedbdf8 100644
--- a/addons/portal_project_issue/portal_project_issue_view.xml
+++ b/addons/portal_project_issue/portal_project_issue_view.xml
@@ -27,6 +27,7 @@
+