Browse Source

Merge pull request #118 from hbrunn/6.1-travis

[FIX] coding style
pull/266/head
Pedro M. Baeza 10 years ago
parent
commit
78c07176dc
  1. 3
      .travis.yml
  2. 10
      dbfilter_from_header/__init__.py
  3. 16
      dbfilter_from_header/__openerp__.py
  4. 2
      email_template_template/__init__.py
  5. 25
      email_template_template/__openerp__.py
  6. 2
      email_template_template/model/__init__.py
  7. 11
      email_template_template/model/email_template.py
  8. 7
      fetchmail_attach_from_folder/__init__.py
  9. 7
      fetchmail_attach_from_folder/__openerp__.py
  10. 9
      fetchmail_attach_from_folder/match_algorithm/__init__.py
  11. 12
      fetchmail_attach_from_folder/match_algorithm/base.py
  12. 16
      fetchmail_attach_from_folder/match_algorithm/email_domain.py
  13. 11
      fetchmail_attach_from_folder/match_algorithm/email_exact.py
  14. 18
      fetchmail_attach_from_folder/match_algorithm/openerp_standard.py
  15. 5
      fetchmail_attach_from_folder/model/__init__.py
  16. 89
      fetchmail_attach_from_folder/model/fetchmail_server.py
  17. 83
      fetchmail_attach_from_folder/model/fetchmail_server_folder.py
  18. 3
      fetchmail_attach_from_folder/wizard/__init__.py
  19. 100
      fetchmail_attach_from_folder/wizard/attach_mail_manually.py
  20. 2
      mail_client_view/__init__.py
  21. 2
      mail_client_view/__openerp__.py
  22. 2
      mail_client_view/model/__init__.py
  23. 7
      mail_client_view/model/mail_message.py
  24. 11
      mail_environment/__openerp__.py
  25. 70
      mail_environment/env_mail.py
  26. 8
      mass_editing/__init__.py
  27. 21
      mass_editing/__openerp__.py
  28. 95
      mass_editing/mass_editing.py
  29. 5
      mass_editing/wizard/__init__.py
  30. 268
      mass_editing/wizard/mass_editing_wizard.py
  31. 6
      security_protector/__openerp__.py
  32. 29
      security_protector/security_protector.py
  33. 3
      server_env_base_external_referentials/__init__.py
  34. 11
      server_env_base_external_referentials/__openerp__.py
  35. 26
      server_env_base_external_referentials/base_external_referentials.py
  36. 14
      server_environment/serv_config.py
  37. 2
      super_calendar/__init__.py
  38. 37
      super_calendar/__openerp__.py
  39. 137
      super_calendar/super_calendar.py
  40. 2
      unserialize_field/__init__.py
  41. 31
      unserialize_field/ir_model_fields.py
  42. 2
      view_groups_id/__init__.py
  43. 9
      view_groups_id/__openerp__.py
  44. 2
      view_groups_id/model/__init__.py
  45. 28
      view_groups_id/model/ir_ui_view.py

3
.travis.yml

@ -4,8 +4,7 @@ python:
- "2.7" - "2.7"
env: env:
- VERSION="6.1" ODOO_REPO="odoo/odoo"
- VERSION="6.1" UNIT_TEST="1"
- VERSION="6.1" ODOO_REPO="odoo/odoo" EXCLUDE="server_environment"
virtualenv: virtualenv:
system_site_packages: true system_site_packages: true

10
dbfilter_from_header/__init__.py

@ -24,12 +24,16 @@ from openerp.addons.web.common.http import jsonrequest
get_list_org = Database.get_list.__closure__[0].cell_contents get_list_org = Database.get_list.__closure__[0].cell_contents
@jsonrequest @jsonrequest
def get_list(self, req): def get_list(self, req):
db_filter = req.httprequest.environ.get('HTTP_X_OPENERP_DBFILTER', '.*') db_filter = req.httprequest.environ.get('HTTP_X_OPENERP_DBFILTER', '.*')
dbs = get_list_org(self, req) dbs = get_list_org(self, req)
return {'db_list': [db for db in
dbs.get('db_list', [])
if re.match(db_filter, db)]}
return {
'db_list': [
db for db in dbs.get('db_list', [])
if re.match(db_filter, db)
]
}
Database.get_list = get_list Database.get_list = get_list

16
dbfilter_from_header/__openerp__.py

@ -19,9 +19,9 @@
# #
############################################################################## ##############################################################################
{ {
"name" : "dbfilter_from_header",
"version" : "1.0",
"author" : "Therp BV",
"name": "dbfilter_from_header",
"version": "1.0",
"author": "Therp BV",
"complexity": "normal", "complexity": "normal",
"description": """ "description": """
This addon lets you pass a dbfilter as a HTTP header. This addon lets you pass a dbfilter as a HTTP header.
@ -34,11 +34,11 @@
The addon has to be loaded as server-wide module. The addon has to be loaded as server-wide module.
""", """,
"category" : "Tools",
"depends" : [
"category": "Tools",
"depends": [
'web', 'web',
], ],
"data" : [
"data": [
], ],
"js": [ "js": [
], ],
@ -46,7 +46,7 @@
], ],
"auto_install": False, "auto_install": False,
"installable": True, "installable": True,
"external_dependencies" : {
'python' : [],
"external_dependencies": {
'python': [],
}, },
} }

2
email_template_template/__init__.py

@ -1 +1 @@
import model
from . import model

25
email_template_template/__openerp__.py

@ -26,11 +26,11 @@
'complexity': "expert", 'complexity': "expert",
"description": """If an organisation's email layout is a bit more "description": """If an organisation's email layout is a bit more
complicated, changes can be tedious when having to do that across several email complicated, changes can be tedious when having to do that across several email
templates. So this addon allows to define templates for mails that is referenced
by other mail templates.
This way we can put the layout parts into the template template and only content
in the other templates. Changing the layout is then only a matter of changing
the template template.
templates. So this addon allows to define templates for mails that is
referenced by other mail templates.
This way we can put the layout parts into the template template and only
content in the other templates. Changing the layout is then only a matter of
changing the template template.
Usage: Usage:
@ -46,7 +46,8 @@ For example, create a template template
----- -----
Example Corp logo Example Corp logo
Example Corp header Example Corp header
${object.body_text} <- this gets evaluated to the body_text of a template using this template template
${object.body_text} <- this gets evaluated to the body_text of a template using
this template template
Example Corp Example Corp
Example street 42 Example street 42
Example city Example city
@ -58,7 +59,8 @@ Then in your template you write
----- -----
Dear ${object.partner_id.name}, Dear ${object.partner_id.name},
Your order has been booked on date ${object.date} for a total amount of ${object.sum}.
Your order has been booked on date ${object.date} for a total amount of
${object.sum}.
----- -----
And it will be evaluated to And it will be evaluated to
@ -75,12 +77,17 @@ Example city
Example Corp footer Example Corp footer
----- -----
Given the way evaluation works internally (body_text of the template template is evaluated two times, first with the instance of email.template of your own template, then with the object your template refers to), you can do some trickery if you know that a template template is always used with the same kind of model (that is, models that have the same field name):
Given the way evaluation works internally (body_text of the template template
is evaluated two times, first with the instance of email.template of your own
template, then with the object your template refers to), you can do some
trickery if you know that a template template is always used with the same kind
of model (that is, models that have the same field name):
In your template template: In your template template:
------ ------
Dear ${'${object.name}'}, <-- gets evaluated to "${object.name}" in the first step, then to the content of object.name
Dear ${'${object.name}'}, <-- gets evaluated to "${object.name}" in the first
step, then to the content of object.name
${object.body_html} ${object.body_html}
Best, Best,
Example Corp Example Corp

2
email_template_template/model/__init__.py

@ -18,4 +18,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import email_template
from . import email_template

11
email_template_template/model/email_template.py

@ -39,18 +39,19 @@ class email_template(Model):
'is_template_template': fields.function( 'is_template_template': fields.function(
_get_is_template_template, type='boolean', _get_is_template_template, type='boolean',
string='Is a template template'), string='Is a template template'),
}
}
def get_email_template(self, cr, uid, template_id=False, record_id=None, def get_email_template(self, cr, uid, template_id=False, record_id=None,
context=None): context=None):
this = super(email_template, self).get_email_template(
this = super(
email_template, self).get_email_template(
cr, uid, template_id, record_id, context) cr, uid, template_id, record_id, context)
if this.email_template_id and not this.is_template_template: if this.email_template_id and not this.is_template_template:
for field in ['body_html', 'body_text']: for field in ['body_html', 'body_text']:
if this[field] and this.email_template_id[field]: if this[field] and this.email_template_id[field]:
this._data[this.id][field] = self.render_template( this._data[this.id][field] = self.render_template(
cr, uid, this.email_template_id[field],
this.email_template_id.model,
this.id, this._context)
cr, uid, this.email_template_id[field],
this.email_template_id.model,
this.id, this._context)
return this return this

7
fetchmail_attach_from_folder/__init__.py

@ -19,7 +19,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import match_algorithm
import model
import wizard
from . import match_algorithm
from . import model
from . import wizard

7
fetchmail_attach_from_folder/__openerp__.py

@ -24,8 +24,9 @@
'name': 'Attach mails in an IMAP folder to existing objects', 'name': 'Attach mails in an IMAP folder to existing objects',
'version': '1.0.1', 'version': '1.0.1',
'description': """ 'description': """
Adds the possibility to attach emails from a certain IMAP folder to objects,
ie partners. Matching is done via several algorithms, ie email address.
Adds the possibility to attach emails from a certain IMAP folder to
objects, ie partners. Matching is done via several algorithms, ie email
address.
This gives a simple possibility to archive emails in OpenERP without a mail This gives a simple possibility to archive emails in OpenERP without a mail
client integration. client integration.
@ -38,7 +39,7 @@
'view/fetchmail_server.xml', 'view/fetchmail_server.xml',
'wizard/attach_mail_manually.xml', 'wizard/attach_mail_manually.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
],
],
'js': [], 'js': [],
'installable': True, 'installable': True,
'active': False, 'active': False,

9
fetchmail_attach_from_folder/match_algorithm/__init__.py

@ -19,8 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import base
import email_exact
import email_domain
import openerp_standard
from . import base
from . import email_exact
from . import email_domain
from . import openerp_standard

12
fetchmail_attach_from_folder/match_algorithm/base.py

@ -20,16 +20,16 @@
# #
############################################################################## ##############################################################################
class base(object): class base(object):
name = None name = None
'''Name shown to the user'''
'Name shown to the user'
required_fields = [] required_fields = []
'''Fields on fetchmail_server folder that are required for this algorithm'''
'Fields on fetchmail_server folder that are required for this algorithm'
readonly_fields = [] readonly_fields = []
'''Fields on fetchmail_server folder that are readonly for this algorithm'''
'Fields on fetchmail_server folder that are readonly for this algorithm'
def search_matches(self, cr, uid, conf, mail_message, mail_message_org): def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
'''Returns ids found for model with mail_message''' '''Returns ids found for model with mail_message'''
@ -39,5 +39,5 @@ class base(object):
self, cr, uid, connection, object_id, folder, self, cr, uid, connection, object_id, folder,
mail_message, mail_message_org, msgid, context=None): mail_message, mail_message_org, msgid, context=None):
'''Do whatever it takes to handle a match''' '''Do whatever it takes to handle a match'''
return folder.server_id.attach_mail(connection, object_id, folder,
mail_message, msgid)
return folder.server_id.attach_mail(
connection, object_id, folder, mail_message, msgid)

16
fetchmail_attach_from_folder/match_algorithm/email_domain.py

@ -19,8 +19,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from .email_exact import email_exact
from email_exact import email_exact
class email_domain(email_exact): class email_domain(email_exact):
'''Search objects by domain name of email address. '''Search objects by domain name of email address.
@ -29,16 +29,16 @@ class email_domain(email_exact):
def search_matches(self, cr, uid, conf, mail_message, mail_message_org): def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
ids = super(email_domain, self).search_matches( ids = super(email_domain, self).search_matches(
cr, uid, conf, mail_message, mail_message_org)
cr, uid, conf, mail_message, mail_message_org)
if not ids: if not ids:
domains = [] domains = []
for addr in self._get_mailaddresses(conf, mail_message): for addr in self._get_mailaddresses(conf, mail_message):
domains.append(addr.split('@')[-1]) domains.append(addr.split('@')[-1])
ids = conf.pool.get(conf.model_id.model).search( ids = conf.pool.get(conf.model_id.model).search(
cr, uid,
self._get_mailaddress_search_domain(
conf, mail_message,
operator='like',
values=['%@'+domain for domain in set(domains)]),
order=conf.model_order)
cr, uid,
self._get_mailaddress_search_domain(
conf, mail_message,
operator='like',
values=['%@' + domain for domain in set(domains)]),
order=conf.model_order)
return ids return ids

11
fetchmail_attach_from_folder/match_algorithm/email_exact.py

@ -20,10 +20,11 @@
# #
############################################################################## ##############################################################################
from base import base
from .base import base
from openerp.tools.safe_eval import safe_eval from openerp.tools.safe_eval import safe_eval
from openerp.addons.mail.mail_message import to_email from openerp.addons.mail.mail_message import to_email
class email_exact(base): class email_exact(base):
'''Search for exactly the mailadress as noted in the email''' '''Search for exactly the mailadress as noted in the email'''
@ -36,17 +37,17 @@ class email_exact(base):
for field in fields: for field in fields:
if field in mail_message: if field in mail_message:
mailaddresses += to_email(mail_message[field]) mailaddresses += to_email(mail_message[field])
return [ addr.lower() for addr in mailaddresses ]
return [addr.lower() for addr in mailaddresses]
def _get_mailaddress_search_domain( def _get_mailaddress_search_domain(
self, conf, mail_message, operator='=', values=None): self, conf, mail_message, operator='=', values=None):
mailaddresses = values or self._get_mailaddresses( mailaddresses = values or self._get_mailaddresses(
conf, mail_message)
conf, mail_message)
if not mailaddresses: if not mailaddresses:
return [(0, '=', 1)] return [(0, '=', 1)]
search_domain = ((['|'] * (len(mailaddresses) - 1)) + [ search_domain = ((['|'] * (len(mailaddresses) - 1)) + [
(conf.model_field, operator, addr) for addr in mailaddresses] +
safe_eval(conf.domain or '[]'))
(conf.model_field, operator, addr) for addr in mailaddresses] +
safe_eval(conf.domain or '[]'))
return search_domain return search_domain
def search_matches(self, cr, uid, conf, mail_message, mail_message_org): def search_matches(self, cr, uid, conf, mail_message, mail_message_org):

18
fetchmail_attach_from_folder/match_algorithm/openerp_standard.py

@ -19,17 +19,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from .base import base
from base import base
from openerp.tools.safe_eval import safe_eval
class openerp_standard(base): class openerp_standard(base):
'''No search at all. Use OpenERP's standard mechanism to attach mails to '''No search at all. Use OpenERP's standard mechanism to attach mails to
mail.thread objects. Note that this algorithm always matches.''' mail.thread objects. Note that this algorithm always matches.'''
name = 'OpenERP standard' name = 'OpenERP standard'
readonly_fields = ['model_field', 'mail_field', 'match_first', 'domain',
'model_order', 'flag_nonmatching']
readonly_fields = [
'model_field', 'mail_field', 'match_first', 'domain', 'model_order',
'flag_nonmatching']
def search_matches(self, cr, uid, conf, mail_message, mail_message_org): def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
'''Always match. Duplicates will be fished out by message_id''' '''Always match. Duplicates will be fished out by message_id'''
@ -39,11 +39,11 @@ class openerp_standard(base):
self, cr, uid, connection, object_id, folder, self, cr, uid, connection, object_id, folder,
mail_message, mail_message_org, msgid, context): mail_message, mail_message_org, msgid, context):
result = folder.pool.get('mail.thread').message_process( result = folder.pool.get('mail.thread').message_process(
cr, uid,
folder.model_id.model, mail_message_org,
save_original=folder.server_id.original,
strip_attachments=(not folder.server_id.attach),
context=context)
cr, uid,
folder.model_id.model, mail_message_org,
save_original=folder.server_id.original,
strip_attachments=(not folder.server_id.attach),
context=context)
if folder.delete_matching: if folder.delete_matching:
connection.store(msgid, '+FLAGS', '\\DELETED') connection.store(msgid, '+FLAGS', '\\DELETED')

5
fetchmail_attach_from_folder/model/__init__.py

@ -19,6 +19,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import fetchmail_server
import fetchmail_server_folder
from . import fetchmail_server
from . import fetchmail_server_folder

89
fetchmail_attach_from_folder/model/fetchmail_server.py

@ -23,12 +23,11 @@
import base64 import base64
import simplejson import simplejson
from lxml import etree from lxml import etree
from openerp.osv.orm import Model, except_orm, browse_null
from openerp.osv.orm import Model, except_orm
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.osv import fields from openerp.osv import fields
from openerp.addons.fetchmail.fetchmail import logger from openerp.addons.fetchmail.fetchmail import logger
from openerp.tools.misc import UnquoteEvalContext from openerp.tools.misc import UnquoteEvalContext
from openerp.tools.safe_eval import safe_eval
class fetchmail_server(Model): class fetchmail_server(Model):
@ -36,7 +35,7 @@ class fetchmail_server(Model):
_columns = { _columns = {
'folder_ids': fields.one2many( 'folder_ids': fields.one2many(
'fetchmail.server.folder', 'server_id', 'Folders'),
'fetchmail.server.folder', 'server_id', 'Folders'),
} }
_defaults = { _defaults = {
@ -45,7 +44,7 @@ class fetchmail_server(Model):
def __init__(self, pool, cr): def __init__(self, pool, cr):
self._columns['object_id'].required = False self._columns['object_id'].required = False
return super(fetchmail_server, self).__init__(pool, cr)
super(fetchmail_server, self).__init__(pool, cr)
def onchange_server_type( def onchange_server_type(
self, cr, uid, ids, server_type=False, ssl=False, self, cr, uid, ids, server_type=False, ssl=False,
@ -96,22 +95,23 @@ class fetchmail_server(Model):
if connection.select(folder.path)[0] != 'OK': if connection.select(folder.path)[0] != 'OK':
logger.error( logger.error(
'Could not open mailbox %s on %s' % ( 'Could not open mailbox %s on %s' % (
folder.path, this.server))
folder.path, this.server))
connection.select() connection.select()
continue continue
result, msgids = this.get_msgids(connection) result, msgids = this.get_msgids(connection)
if result != 'OK': if result != 'OK':
logger.error( logger.error(
'Could not search mailbox %s on %s' % ( 'Could not search mailbox %s on %s' % (
folder.path, this.server))
folder.path, this.server))
continue continue
for msgid in msgids[0].split(): for msgid in msgids[0].split():
matched_object_ids += this.apply_matching( matched_object_ids += this.apply_matching(
connection, folder, msgid, match_algorithm)
connection, folder, msgid, match_algorithm)
logger.info('finished checking for emails in %s server %s',
folder.path, this.name)
logger.info(
'finished checking for emails in %s server %s',
folder.path, this.name)
return matched_object_ids return matched_object_ids
@ -130,15 +130,16 @@ class fetchmail_server(Model):
if result != 'OK': if result != 'OK':
logger.error( logger.error(
'Could not fetch %s in %s on %s' % (
msgid, folder.path, this.server))
'Could not fetch %s in %s on %s',
msgid, folder.path, this.server)
continue continue
mail_message = self.pool.get('mail.message').parse_message( mail_message = self.pool.get('mail.message').parse_message(
msgdata[0][1], this.original) msgdata[0][1], this.original)
if self.pool.get('mail.message').search(cr, uid, [
('message_id', '=', mail_message['message-id'])]):
if self.pool.get('mail.message').search(
cr, uid,
[('message_id', '=', mail_message['message-id'])]):
continue continue
found_ids = match_algorithm.search_matches( found_ids = match_algorithm.search_matches(
@ -155,7 +156,7 @@ class fetchmail_server(Model):
msgdata[0][1], msgid, context) msgdata[0][1], msgid, context)
cr.execute('release savepoint apply_matching') cr.execute('release savepoint apply_matching')
matched_object_ids += found_ids[:1] matched_object_ids += found_ids[:1]
except Exception, e:
except Exception:
cr.execute('rollback to savepoint apply_matching') cr.execute('rollback to savepoint apply_matching')
logger.exception( logger.exception(
"Failed to fetch mail %s from %s", "Failed to fetch mail %s from %s",
@ -180,48 +181,48 @@ class fetchmail_server(Model):
partner_id = self.pool.get( partner_id = self.pool.get(
folder.model_id.model).browse( folder.model_id.model).browse(
cr, uid, object_id, context cr, uid, object_id, context
).partner_id.id
).partner_id.id
attachments=[]
attachments = []
if this.attach and mail_message.get('attachments'): if this.attach and mail_message.get('attachments'):
for attachment in mail_message['attachments']: for attachment in mail_message['attachments']:
fname, fcontent = attachment fname, fcontent = attachment
if isinstance(fcontent, unicode): if isinstance(fcontent, unicode):
fcontent = fcontent.encode('utf-8') fcontent = fcontent.encode('utf-8')
data_attach = { data_attach = {
'name': fname,
'datas': base64.b64encode(str(fcontent)),
'datas_fname': fname,
'description': _('Mail attachment'),
'res_model': folder.model_id.model,
'res_id': object_id,
}
'name': fname,
'datas': base64.b64encode(str(fcontent)),
'datas_fname': fname,
'description': _('Mail attachment'),
'res_model': folder.model_id.model,
'res_id': object_id,
}
attachments.append( attachments.append(
self.pool.get('ir.attachment').create( self.pool.get('ir.attachment').create(
cr, uid, data_attach, context=context)) cr, uid, data_attach, context=context))
mail_message_ids.append( mail_message_ids.append(
self.pool.get('mail.message').create(
cr, uid,
{
'partner_id': partner_id,
'model': folder.model_id.model,
'res_id': object_id,
'body_text': mail_message.get('body'),
'body_html': mail_message.get('body_html'),
'subject': mail_message.get('subject') or '',
'email_to': mail_message.get('to'),
'email_from': mail_message.get('from'),
'email_cc': mail_message.get('cc'),
'reply_to': mail_message.get('reply'),
'date': mail_message.get('date'),
'message_id': mail_message.get('message-id'),
'subtype': mail_message.get('subtype'),
'headers': mail_message.get('headers'),
'state': folder.msg_state,
'attachment_ids': [(6, 0, attachments)],
},
context))
self.pool.get('mail.message').create(
cr, uid,
{
'partner_id': partner_id,
'model': folder.model_id.model,
'res_id': object_id,
'body_text': mail_message.get('body'),
'body_html': mail_message.get('body_html'),
'subject': mail_message.get('subject') or '',
'email_to': mail_message.get('to'),
'email_from': mail_message.get('from'),
'email_cc': mail_message.get('cc'),
'reply_to': mail_message.get('reply'),
'date': mail_message.get('date'),
'message_id': mail_message.get('message-id'),
'subtype': mail_message.get('subtype'),
'headers': mail_message.get('headers'),
'state': folder.msg_state,
'attachment_ids': [(6, 0, attachments)],
},
context))
if folder.delete_matching: if folder.delete_matching:
connection.store(msgid, '+FLAGS', '\\DELETED') connection.store(msgid, '+FLAGS', '\\DELETED')

83
fetchmail_attach_from_folder/model/fetchmail_server_folder.py

@ -31,9 +31,10 @@ class fetchmail_server_folder(Model):
def _get_match_algorithms(self): def _get_match_algorithms(self):
def get_all_subclasses(cls): def get_all_subclasses(cls):
return cls.__subclasses__() + [subsub
for sub in cls.__subclasses__()
for subsub in get_all_subclasses(sub)]
return cls.__subclasses__() + [
subsub
for sub in cls.__subclasses__()
for subsub in get_all_subclasses(sub)]
return dict([(cls.__name__, cls) for cls in get_all_subclasses( return dict([(cls.__name__, cls) for cls in get_all_subclasses(
match_algorithm.base.base)]) match_algorithm.base.base)])
@ -47,55 +48,55 @@ class fetchmail_server_folder(Model):
_columns = { _columns = {
'sequence': fields.integer('Sequence'), 'sequence': fields.integer('Sequence'),
'path': fields.char( 'path': fields.char(
'Path', size=256, help='The path to your mail '
"folder. Typically would be something like 'INBOX.myfolder'",
required=True),
'Path', size=256, help='The path to your mail '
"folder. Typically would be something like 'INBOX.myfolder'",
required=True),
'model_id': fields.many2one( 'model_id': fields.many2one(
'ir.model', 'Model', required=True,
help='The model to attach emails to'),
'ir.model', 'Model', required=True,
help='The model to attach emails to'),
'model_field': fields.char( 'model_field': fields.char(
'Field (model)', size=128,
help='The field in your model that contains the field to match '
'against.\n'
'Examples:\n'
"'email' if your model is res.partner, or "
"'partner_id.email' if you're matching sale orders"),
'Field (model)', size=128,
help='The field in your model that contains the field to match '
'against.\n'
'Examples:\n'
"'email' if your model is res.partner, or "
"'partner_id.email' if you're matching sale orders"),
'model_order': fields.char( 'model_order': fields.char(
'Order (model)', size=128,
help='Fields to order by, this mostly useful in conjunction '
"with 'Use 1st match'"),
'Order (model)', size=128,
help='Fields to order by, this mostly useful in conjunction '
"with 'Use 1st match'"),
'match_algorithm': fields.selection( 'match_algorithm': fields.selection(
_get_match_algorithms_sel,
'Match algorithm', required=True, translate=True,
help='The algorithm used to determine which object an email '
'matches.'),
_get_match_algorithms_sel,
'Match algorithm', required=True, translate=True,
help='The algorithm used to determine which object an email '
'matches.'),
'mail_field': fields.char( 'mail_field': fields.char(
'Field (email)', size=128,
help='The field in the email used for matching. Typically '
"this is 'to' or 'from'"),
'Field (email)', size=128,
help='The field in the email used for matching. Typically '
"this is 'to' or 'from'"),
'server_id': fields.many2one('fetchmail.server', 'Server'), 'server_id': fields.many2one('fetchmail.server', 'Server'),
'delete_matching': fields.boolean( 'delete_matching': fields.boolean(
'Delete matches',
help='Delete matched emails from server'),
'Delete matches',
help='Delete matched emails from server'),
'flag_nonmatching': fields.boolean( 'flag_nonmatching': fields.boolean(
'Flag nonmatching',
help="Flag emails in the server that don't match any object "
'in OpenERP'),
'Flag nonmatching',
help="Flag emails in the server that don't match any object "
'in OpenERP'),
'match_first': fields.boolean( 'match_first': fields.boolean(
'Use 1st match',
help='If there are multiple matches, use the first one. If '
'not checked, multiple matches count as no match at all'),
'Use 1st match',
help='If there are multiple matches, use the first one. If '
'not checked, multiple matches count as no match at all'),
'domain': fields.char( 'domain': fields.char(
'Domain', size=128, help='Fill in a search '
'filter to narrow down objects to match'),
'Domain', size=128, help='Fill in a search '
'filter to narrow down objects to match'),
'msg_state': fields.selection( 'msg_state': fields.selection(
[
('sent', 'Sent'),
('received', 'Received'),
],
'Message state',
help='The state messages fetched from this folder should be '
'assigned in OpenERP'),
[
('sent', 'Sent'),
('received', 'Received'),
],
'Message state',
help='The state messages fetched from this folder should be '
'assigned in OpenERP'),
} }
_defaults = { _defaults = {

3
fetchmail_attach_from_folder/wizard/__init__.py

@ -19,5 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import attach_mail_manually
from . import attach_mail_manually

100
fetchmail_attach_from_folder/wizard/attach_mail_manually.py

@ -20,6 +20,7 @@
# #
############################################################################## ##############################################################################
import logging
from openerp.osv import fields from openerp.osv import fields
from openerp.osv.orm import TransientModel from openerp.osv.orm import TransientModel
@ -28,46 +29,53 @@ class attach_mail_manually(TransientModel):
_name = 'fetchmail.attach.mail.manually' _name = 'fetchmail.attach.mail.manually'
_columns = { _columns = {
'folder_id': fields.many2one('fetchmail.server.folder', 'Folder',
readonly=True),
'mail_ids': fields.one2many(
'fetchmail.attach.mail.manually.mail', 'wizard_id', 'Emails'),
}
'folder_id': fields.many2one(
'fetchmail.server.folder', 'Folder', readonly=True),
'mail_ids': fields.one2many(
'fetchmail.attach.mail.manually.mail', 'wizard_id', 'Emails'),
}
def default_get(self, cr, uid, fields_list, context=None): def default_get(self, cr, uid, fields_list, context=None):
if context is None: if context is None:
context = {} context = {}
defaults = super(attach_mail_manually, self).default_get(cr, uid,
fields_list, context)
defaults = super(attach_mail_manually, self).default_get(
cr, uid, fields_list, context)
for folder in self.pool.get('fetchmail.server.folder').browse(cr, uid,
[context.get('default_folder_id')], context):
defaults['mail_ids']=[]
for folder in self.pool.get('fetchmail.server.folder').browse(
cr, uid, [context.get('default_folder_id')], context):
defaults['mail_ids'] = []
connection = folder.server_id.connect() connection = folder.server_id.connect()
connection.select(folder.path) connection.select(folder.path)
result, msgids = connection.search(None,
'FLAGGED' if folder.flag_nonmatching else 'UNDELETED')
result, msgids = connection.search(
None,
'FLAGGED' if folder.flag_nonmatching else 'UNDELETED')
if result != 'OK': if result != 'OK':
logger.error('Could not search mailbox %s on %s' % (
folder.path, this.server))
logging.error(
'Could not search mailbox %s on %s',
folder.path,
folder.server_id.server)
continue continue
attach_mail_manually_mail._columns['object_id'].selection=[
(folder.model_id.model, folder.model_id.name)]
attach_mail_manually_mail._columns['object_id'].selection = [
(folder.model_id.model, folder.model_id.name)
]
for msgid in msgids[0].split(): for msgid in msgids[0].split():
result, msgdata = connection.fetch(msgid, '(RFC822)') result, msgdata = connection.fetch(msgid, '(RFC822)')
if result != 'OK': if result != 'OK':
logger.error('Could not fetch %s in %s on %s' % (
msgid, folder.path, this.server))
logging.error(
'Could not fetch %s in %s on %s',
msgid, folder.path, folder.server_id.server)
continue continue
mail_message = self.pool.get('mail.message').parse_message( mail_message = self.pool.get('mail.message').parse_message(
msgdata[0][1])
defaults['mail_ids'].append((0, 0, {
'msgid': msgid,
'subject': mail_message.get('subject', ''),
'date': mail_message.get('date', ''),
'object_id': folder.model_id.model+',False'
}))
msgdata[0][1])
defaults['mail_ids'].append(
(0, 0,
{
'msgid': msgid,
'subject': mail_message.get('subject', ''),
'date': mail_message.get('date', ''),
'object_id': folder.model_id.model + ',False'
}))
connection.close() connection.close()
return defaults return defaults
@ -79,32 +87,38 @@ class attach_mail_manually(TransientModel):
connection.select(this.folder_id.path) connection.select(this.folder_id.path)
result, msgdata = connection.fetch(mail.msgid, '(RFC822)') result, msgdata = connection.fetch(mail.msgid, '(RFC822)')
if result != 'OK': if result != 'OK':
logger.error('Could not fetch %s in %s on %s' % (
msgid, folder.path, this.server))
logging.error(
'Could not fetch %s in %s on %s',
mail.msgid, this.folder_id.path,
this.folder_id.server_id.server)
continue continue
mail_message = self.pool.get('mail.message').parse_message( mail_message = self.pool.get('mail.message').parse_message(
msgdata[0][1], this.folder_id.server_id.original)
msgdata[0][1], this.folder_id.server_id.original)
this.folder_id.server_id.attach_mail(connection,
mail.object_id.id, this.folder_id, mail_message,
mail.msgid)
this.folder_id.server_id.attach_mail(
connection,
mail.object_id.id, this.folder_id, mail_message,
mail.msgid)
connection.close() connection.close()
return {'type': 'ir.actions.act_window_close'} return {'type': 'ir.actions.act_window_close'}
class attach_mail_manually_mail(TransientModel): class attach_mail_manually_mail(TransientModel):
_name = 'fetchmail.attach.mail.manually.mail' _name = 'fetchmail.attach.mail.manually.mail'
_columns = { _columns = {
'wizard_id': fields.many2one('fetchmail.attach.mail.manually',
readonly=True),
'msgid': fields.char('Message id', size=16, readonly=True),
'subject': fields.char('Subject', size=128, readonly=True),
'date': fields.datetime('Date', readonly=True),
'object_id': fields.reference('Object',
selection=lambda self, cr, uid, context:
[(m.model, m.name) for m in
self.pool.get('ir.model').browse(cr, uid,
self.pool.get('ir.model').search(cr, uid, []),
context)], size=128),
}
'wizard_id': fields.many2one(
'fetchmail.attach.mail.manually', readonly=True),
'msgid': fields.char('Message id', size=16, readonly=True),
'subject': fields.char('Subject', size=128, readonly=True),
'date': fields.datetime('Date', readonly=True),
'object_id': fields.reference(
'Object',
selection=lambda self, cr, uid, context:
[(m.model, m.name) for m in
self.pool.get('ir.model').browse(
cr, uid, self.pool.get('ir.model').search(cr, uid, []),
context)],
size=128),
}

2
mail_client_view/__init__.py

@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import model
from . import model

2
mail_client_view/__openerp__.py

@ -40,7 +40,7 @@
'data': [ 'data': [
'view/mail_user_menu.xml', 'view/mail_user_menu.xml',
'view/mail_user_view.xml', 'view/mail_user_view.xml',
],
],
'js': [], 'js': [],
'installable': True, 'installable': True,
'active': False, 'active': False,

2
mail_client_view/model/__init__.py

@ -1 +1 @@
import mail_message
from . import mail_message

7
mail_client_view/model/mail_message.py

@ -25,15 +25,16 @@ class mail_message(Model):
def create(self, cr, user, vals, context=None): def create(self, cr, user, vals, context=None):
# Set newly received messages as needing action, unless an # Set newly received messages as needing action, unless an
# explicit value for action_needed has been passed. # explicit value for action_needed has been passed.
if ((not 'action_needed' in vals)
and ('state' in vals) and (vals['state'] == 'received')):
if ('action_needed' not in vals
and ('state' in vals) and (vals['state'] == 'received')):
vals['action_needed'] = True vals['action_needed'] = True
mm_id = super(mail_message, self).create( mm_id = super(mail_message, self).create(
cr, user, vals, context=context) cr, user, vals, context=context)
return mm_id return mm_id
_columns = { _columns = {
'action_needed': fields.boolean('Action needed',
'action_needed': fields.boolean(
'Action needed',
help='Action needed is True whenever a new mail is received, or' help='Action needed is True whenever a new mail is received, or'
' when a user flags a message as needing attention.'), ' when a user flags a message as needing attention.'),
} }

11
mail_environment/__openerp__.py

@ -26,8 +26,10 @@
'description': """ 'description': """
Extend mail and fetch mail with server environment module. Extend mail and fetch mail with server environment module.
In config files, sections outgoint_mail and incoming_mails are default values for all Outgoing Mail Servers and Fetchmail Servers.
For each server, you can (re)define values with a section named "outgoing_mail.resource_name" where resource_name is the name of your server.
In config files, sections outgoint_mail and incoming_mails are default values
for all Outgoing Mail Servers and Fetchmail Servers.
For each server, you can (re)define values with a section named
"outgoing_mail.resource_name" where resource_name is the name of your server.
Exemple of config file : Exemple of config file :
@ -57,7 +59,10 @@ password = openerp
'author': 'Camptocamp', 'author': 'Camptocamp',
'license': 'AGPL-3', 'license': 'AGPL-3',
'website': 'http://openerp.camptocamp.com', 'website': 'http://openerp.camptocamp.com',
'depends': ['mail', 'fetchmail', 'server_environment', 'server_environment_files', 'crm'],
'depends': [
'mail', 'fetchmail', 'server_environment', 'server_environment_files',
'crm'
],
'init_xml': [], 'init_xml': [],
'update_xml': ['mail_view.xml'], 'update_xml': ['mail_view.xml'],
'demo_xml': [], 'demo_xml': [],

70
mail_environment/env_mail.py

@ -41,7 +41,8 @@ class IrMail(osv.osv):
if serv_config.has_section(global_section_name): if serv_config.has_section(global_section_name):
config_vals.update((serv_config.items(global_section_name))) config_vals.update((serv_config.items(global_section_name)))
custom_section_name = '.'.join((global_section_name, mail_server.name))
custom_section_name = '.'.join(
(global_section_name, mail_server.name))
if serv_config.has_section(custom_section_name): if serv_config.has_section(custom_section_name):
config_vals.update(serv_config.items(custom_section_name)) config_vals.update(serv_config.items(custom_section_name))
@ -63,32 +64,39 @@ class IrMail(osv.osv):
string='SMTP Port', string='SMTP Port',
type="integer", type="integer",
multi='outgoing_mail_config', multi='outgoing_mail_config',
help="SMTP Port. Usually 465 for SSL, and 25 or 587 for other cases.",
help="SMTP Port. Usually 465 for SSL, "
"and 25 or 587 for other cases.",
size=5), size=5),
'smtp_user': fields.function(_get_smtp_conf, 'smtp_user': fields.function(_get_smtp_conf,
method=True, method=True,
string='Username', string='Username',
type="char", type="char",
multi='outgoing_mail_config', multi='outgoing_mail_config',
help="Optional username for SMTP authentication",
help="Optional username for SMTP "
"authentication",
size=64), size=64),
'smtp_pass': fields.function(_get_smtp_conf, 'smtp_pass': fields.function(_get_smtp_conf,
method=True, method=True,
string='Password', string='Password',
type="char", type="char",
multi='outgoing_mail_config', multi='outgoing_mail_config',
help="Optional password for SMTP authentication",
help="Optional password for SMTP "
"authentication",
size=64), size=64),
'smtp_encryption' :fields.function(_get_smtp_conf,
method=True,
string='smtp_encryption',
type="char",
multi='outgoing_mail_config',
help="Choose the connection encryption scheme:\n"
"- none: SMTP sessions are done in cleartext.\n"
"- starttls: TLS encryption is requested at start of SMTP session (Recommended)\n"
"- ssl: SMTP sessions are encrypted with SSL/TLS through a dedicated port (default: 465)",
size=64)}
'smtp_encryption': fields.function(
_get_smtp_conf,
method=True,
string='smtp_encryption',
type="char",
multi='outgoing_mail_config',
help="Choose the connection encryption scheme:\n"
"- none: SMTP sessions are done in cleartext.\n"
"- starttls: TLS encryption is requested at start of SMTP session "
"(Recommended)\n"
"- ssl: SMTP sessions are encrypted with SSL/TLS through a "
"dedicated port (default: 465)",
size=64),
}
IrMail() IrMail()
@ -105,10 +113,12 @@ class FetchmailServer(osv.osv):
for fetchmail in self.browse(cursor, uid, ids): for fetchmail in self.browse(cursor, uid, ids):
global_section_name = 'incoming_mail' global_section_name = 'incoming_mail'
key_types = {'port': int,
'is_ssl': lambda a: bool(int(a)),
'attach': lambda a: bool(int(a)),
'original': lambda a: bool(int(a)),}
key_types = {
'port': int,
'is_ssl': lambda a: bool(int(a)),
'attach': lambda a: bool(int(a)),
'original': lambda a: bool(int(a)),
}
# default vals # default vals
config_vals = {'port': 993, config_vals = {'port': 993,
@ -118,7 +128,8 @@ class FetchmailServer(osv.osv):
if serv_config.has_section(global_section_name): if serv_config.has_section(global_section_name):
config_vals.update(serv_config.items(global_section_name)) config_vals.update(serv_config.items(global_section_name))
custom_section_name = '.'.join((global_section_name, fetchmail.name))
custom_section_name = '.'.join(
(global_section_name, fetchmail.name))
if serv_config.has_section(custom_section_name): if serv_config.has_section(custom_section_name):
config_vals.update(serv_config.items(custom_section_name)) config_vals.update(serv_config.items(custom_section_name))
@ -134,7 +145,8 @@ class FetchmailServer(osv.osv):
string='Server', string='Server',
type="char", type="char",
multi='income_mail_config', multi='income_mail_config',
size=256, help="Hostname or IP of the mail server"),
size=256,
help="Hostname or IP of the mail server"),
'port': fields.function(_get_incom_conf, 'port': fields.function(_get_incom_conf,
method=True, method=True,
string='Port', string='Port',
@ -153,22 +165,28 @@ class FetchmailServer(osv.osv):
string='Is SSL', string='Is SSL',
type="boolean", type="boolean",
multi='income_mail_config', multi='income_mail_config',
help='Connections are encrypted with SSL/TLS through'
' a dedicated port (default: IMAPS=993, POP3S=995)'),
help='Connections are encrypted with '
'SSL/TLS through a dedicated port (default: '
'IMAPS=993, POP3S=995)'),
'attach': fields.function(_get_incom_conf, 'attach': fields.function(_get_incom_conf,
method=True, method=True,
string='Keep Attachments', string='Keep Attachments',
type="boolean", type="boolean",
multi='income_mail_config', multi='income_mail_config',
help="Whether attachments should be downloaded. "
"If not enabled, incoming emails will be stripped of any attachments before being processed"),
help="Whether attachments should be "
"downloaded. If not enabled, incoming "
"emails will be stripped of any attachments "
"before being processed"),
'original': fields.function(_get_incom_conf, 'original': fields.function(_get_incom_conf,
method=True, method=True,
string='Keep Original', string='Keep Original',
type="boolean", type="boolean",
multi='income_mail_config', multi='income_mail_config',
help="Whether a full original copy of each email should be kept for reference"
"and attached to each processed message. This will usually double the size of your message database."),
help="Whether a full original copy of "
"each email should be kept for reference"
"and attached to each processed message. "
"This will usually double the size of "
"your message database."),
'user': fields.function(_get_incom_conf, 'user': fields.function(_get_incom_conf,
method=True, method=True,
string='Username', string='Username',

8
mass_editing/__init__.py

@ -2,7 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2012 Serpent Consulting Services (<http://www.serpentcs.com>)
# Copyright (C) 2012 Serpent Consulting Services
# (<http://www.serpentcs.com>)
# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -20,8 +21,7 @@
# #
############################################################################## ##############################################################################
import mass_editing
import wizard
from . import mass_editing
from . import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

21
mass_editing/__openerp__.py

@ -2,7 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2012 Serpent Consulting Services (<http://www.serpentcs.com>)
# Copyright (C) 2012 Serpent Consulting Services
# (<http://www.serpentcs.com>)
# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -19,19 +20,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################
{ {
"name" : "Mass Editing",
"version" : "1.1",
"author" : "Serpent Consulting Services",
"category" : "Tools",
"website" : "http://www.serpentcs.com",
"description": """This module provides the functionality to add, update or remove the values of more than one records on the fly at the same time.
"name": "Mass Editing",
"version": "1.1",
"author": "Serpent Consulting Services",
"category": "Tools",
"website": "http://www.serpentcs.com",
"description": """This module provides the functionality to add, update or
remove the values of more than one records on the fly at the same time.
You can configure mass editing for any OpenERP model. You can configure mass editing for any OpenERP model.
The video explaining the feature is available at http://t.co/wukYMx1A The video explaining the feature is available at http://t.co/wukYMx1A
The menu is now Under Settings/Configuration. The menu is now Under Settings/Configuration.
For more details/customization/feedback contact us on contact@serpentcs.com.
For more details/customization/feedback contact us on
contact@serpentcs.com.
""", """,
'depends': ['base'], 'depends': ['base'],
'init_xml': [], 'init_xml': [],

95
mass_editing/mass_editing.py

@ -2,7 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2012 Serpent Consulting Services (<http://www.serpentcs.com>)
# Copyright (C) 2012 Serpent Consulting Services
# (<http://www.serpentcs.com>)
# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -19,23 +20,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################
from osv import fields, osv from osv import fields, osv
from tools.translate import _ from tools.translate import _
class mass_object(osv.osv): class mass_object(osv.osv):
_name = "mass.object" _name = "mass.object"
_columns = { _columns = {
'name' : fields.char("Name", size=64, required=True, select=1),
'model_id' : fields.many2one('ir.model', 'Model', required=True, select=1),
'field_ids' : fields.many2many('ir.model.fields', 'mass_field_rel', 'mass_id', 'field_id', 'Fields'),
'ref_ir_act_window':fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True,
help="Sidebar action to make this template available on records "
"of the related document model"),
'ref_ir_value':fields.many2one('ir.values', 'Sidebar button', readonly=True,
help="Sidebar button to open the sidebar action"),
'name': fields.char("Name", size=64, required=True, select=1),
'model_id': fields.many2one(
'ir.model', 'Model', required=True, select=1),
'field_ids': fields.many2many(
'ir.model.fields', 'mass_field_rel', 'mass_id', 'field_id',
'Fields'),
'ref_ir_act_window': fields.many2one(
'ir.actions.act_window', 'Sidebar action', readonly=True,
help="Sidebar action to make this template available on records "
"of the related document model"),
'ref_ir_value': fields.many2one(
'ir.values', 'Sidebar button', readonly=True,
help="Sidebar button to open the sidebar action"),
'model_ids': fields.many2many('ir.model', string='Model List') 'model_ids': fields.many2many('ir.model', string='Model List')
} }
@ -48,7 +53,8 @@ class mass_object(osv.osv):
active_model_obj = self.pool.get(model_data.model) active_model_obj = self.pool.get(model_data.model)
if active_model_obj._inherits: if active_model_obj._inherits:
for key, val in active_model_obj._inherits.items(): for key, val in active_model_obj._inherits.items():
found_model_ids = model_obj.search(cr, uid, [('model', '=', key)])
found_model_ids = model_obj.search(
cr, uid, [('model', '=', key)])
if found_model_ids: if found_model_ids:
model_ids.append(found_model_ids[0]) model_ids.append(found_model_ids[0])
return {'value': {'model_ids': [(6, 0, model_ids)]}} return {'value': {'model_ids': [(6, 0, model_ids)]}}
@ -56,44 +62,57 @@ class mass_object(osv.osv):
def create_action(self, cr, uid, ids, context=None): def create_action(self, cr, uid, ids, context=None):
vals = {} vals = {}
action_obj = self.pool.get('ir.actions.act_window') action_obj = self.pool.get('ir.actions.act_window')
data_obj = self.pool.get('ir.model.data')
for data in self.browse(cr, uid, ids, context=context): for data in self.browse(cr, uid, ids, context=context):
src_obj = data.model_id.model src_obj = data.model_id.model
button_name = _('Mass Editing (%s)') % data.name button_name = _('Mass Editing (%s)') % data.name
vals['ref_ir_act_window'] = action_obj.create(cr, uid, {
'name': button_name,
'type': 'ir.actions.act_window',
'res_model': 'mass.editing.wizard',
'src_model': src_obj,
'view_type': 'form',
'context': "{'mass_editing_object' : %d}" % (data.id),
'view_mode':'form,tree',
'target': 'new',
'auto_refresh':1
}, context)
vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, {
'name': button_name,
'model': src_obj,
'key2': 'client_action_multi',
'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']),
'object': True,
}, context)
self.write(cr, uid, ids, {
'ref_ir_act_window': vals.get('ref_ir_act_window',False),
'ref_ir_value': vals.get('ref_ir_value',False),
}, context)
vals['ref_ir_act_window'] = action_obj.create(
cr, uid,
{
'name': button_name,
'type': 'ir.actions.act_window',
'res_model': 'mass.editing.wizard',
'src_model': src_obj,
'view_type': 'form',
'context': "{'mass_editing_object' : %d}" % (data.id),
'view_mode': 'form,tree',
'target': 'new',
'auto_refresh': 1
},
context)
vals['ref_ir_value'] = self.pool.get('ir.values').create(
cr, uid,
{
'name': button_name,
'model': src_obj,
'key2': 'client_action_multi',
'value': "ir.actions.act_window," + str(
vals['ref_ir_act_window']),
'object': True,
},
context)
self.write(
cr, uid, ids,
{
'ref_ir_act_window': vals.get('ref_ir_act_window', False),
'ref_ir_value': vals.get('ref_ir_value', False),
},
context)
return True return True
def unlink_action(self, cr, uid, ids, context=None): def unlink_action(self, cr, uid, ids, context=None):
for template in self.browse(cr, uid, ids, context=context): for template in self.browse(cr, uid, ids, context=context):
try: try:
if template.ref_ir_act_window: if template.ref_ir_act_window:
self.pool.get('ir.actions.act_window').unlink(cr, uid, template.ref_ir_act_window.id, context)
self.pool.get('ir.actions.act_window').unlink(
cr, uid, template.ref_ir_act_window.id, context)
if template.ref_ir_value: if template.ref_ir_value:
ir_values_obj = self.pool.get('ir.values') ir_values_obj = self.pool.get('ir.values')
ir_values_obj.unlink(cr, uid, template.ref_ir_value.id, context)
ir_values_obj.unlink(
cr, uid, template.ref_ir_value.id, context)
except: except:
raise osv.except_osv(_("Warning"), _("Deletion of the action record failed."))
raise osv.except_osv(
_("Warning"),
_("Deletion of the action record failed."))
return True return True
def unlink(self, cr, uid, ids, context=None): def unlink(self, cr, uid, ids, context=None):

5
mass_editing/wizard/__init__.py

@ -2,7 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2012 Serpent Consulting Services (<http://www.serpentcs.com>)
# Copyright (C) 2012 Serpent Consulting Services
# (<http://www.serpentcs.com>)
# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -20,6 +21,6 @@
# #
############################################################################## ##############################################################################
import mass_editing_wizard
from . import mass_editing_wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

268
mass_editing/wizard/mass_editing_wizard.py

@ -2,7 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2012 Serpent Consulting Services (<http://www.serpentcs.com>)
# Copyright (C) 2012 Serpent Consulting Services
# (<http://www.serpentcs.com>)
# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -21,74 +22,250 @@
############################################################################## ##############################################################################
from osv import osv from osv import osv
from osv import fields
from lxml import etree from lxml import etree
import tools import tools
class mass_editing_wizard(osv.osv_memory): class mass_editing_wizard(osv.osv_memory):
_name = 'mass.editing.wizard' _name = 'mass.editing.wizard'
_columns = { _columns = {
} }
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
result = super(mass_editing_wizard, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
def fields_view_get(self, cr, uid, view_id=None, view_type='form',
context=None, toolbar=False, submenu=False):
result = super(mass_editing_wizard, self).fields_view_get(
cr, uid, view_id, view_type, context, toolbar, submenu)
if context.get('mass_editing_object'): if context.get('mass_editing_object'):
mass_object = self.pool.get('mass.object') mass_object = self.pool.get('mass.object')
editing_data = mass_object.browse(cr, uid, context.get('mass_editing_object'), context)
editing_data = mass_object.browse(
cr, uid, context.get('mass_editing_object'), context)
all_fields = {} all_fields = {}
xml_form = etree.Element('form', {'string': tools.ustr(editing_data.name)})
xml_form = etree.Element(
'form', {'string': tools.ustr(editing_data.name)})
xml_group = etree.SubElement(xml_form, 'group', {'colspan': '4'}) xml_group = etree.SubElement(xml_form, 'group', {'colspan': '4'})
etree.SubElement(xml_group, 'label', {'string': '','colspan': '2'})
etree.SubElement(
xml_group, 'label', {'string': '', 'colspan': '2'})
xml_group = etree.SubElement(xml_form, 'group', {'colspan': '4'}) xml_group = etree.SubElement(xml_form, 'group', {'colspan': '4'})
model_obj = self.pool.get(context.get('active_model')) model_obj = self.pool.get(context.get('active_model'))
for field in editing_data.field_ids: for field in editing_data.field_ids:
if field.ttype == "many2many": if field.ttype == "many2many":
field_info = model_obj.fields_get(cr, uid, [field.name], context)
field_info = model_obj.fields_get(
cr, uid, [field.name], context)
all_fields[field.name] = field_info[field.name] all_fields[field.name] = field_info[field.name]
all_fields["selection_"+field.name] = {'type':'selection', 'string': field_info[field.name]['string'],'selection':[('set','Set'),('remove_m2m','Remove'),('add','Add')]}
xml_group = etree.SubElement(xml_group, 'group', {'colspan': '4'})
etree.SubElement(xml_group, 'separator', {'string': field_info[field.name]['string'],'colspan': '2'})
etree.SubElement(xml_group, 'field', {'name': "selection_"+field.name,'colspan': '2','nolabel':'1'})
etree.SubElement(xml_group, 'field', {'name': field.name, 'colspan':'4', 'nolabel':'1', 'attrs':"{'invisible':[('selection_"+field.name+"','=','remove_m2m')]}"})
all_fields["selection_" + field.name] = {
'type': 'selection',
'string': field_info[field.name]['string'],
'selection': [
('set', 'Set'),
('remove_m2m', 'Remove'),
('add', 'Add')
]
}
xml_group = etree.SubElement(
xml_group, 'group', {'colspan': '4'})
etree.SubElement(
xml_group, 'separator',
{
'string': field_info[field.name]['string'],
'colspan': '2'
})
etree.SubElement(
xml_group, 'field',
{
'name': "selection_" + field.name,
'colspan': '2',
'nolabel': '1'
})
etree.SubElement(
xml_group, 'field',
{
'name': field.name,
'colspan': '4',
'nolabel': '1',
'attrs': "{'invisible':[('selection_" + field.name
+ "','=','remove_m2m')]}"
})
elif field.ttype == "many2one": elif field.ttype == "many2one":
field_info = model_obj.fields_get(cr, uid, [field.name], context)
field_info = model_obj.fields_get(
cr, uid, [field.name], context)
if field_info: if field_info:
all_fields["selection_"+field.name] = {'type':'selection', 'string': field_info[field.name]['string'],'selection':[('set','Set'),('remove','Remove')]}
all_fields[field.name] = {'type':field.ttype, 'string': field.field_description, 'relation': field.relation}
etree.SubElement(xml_group, 'field', {'name': "selection_"+field.name, 'colspan':'2'})
etree.SubElement(xml_group, 'field', {'name': field.name,'nolabel':'1','colspan':'2', 'attrs':"{'invisible':[('selection_"+field.name+"','=','remove')]}"})
all_fields["selection_" + field.name] = {
'type': 'selection',
'string': field_info[field.name]['string'],
'selection': [
('set', 'Set'),
('remove', 'Remove')
]
}
all_fields[field.name] = {
'type': field.ttype,
'string': field.field_description,
'relation': field.relation
}
etree.SubElement(
xml_group, 'field',
{
'name': "selection_" + field.name,
'colspan': '2'
})
etree.SubElement(
xml_group, 'field',
{
'name': field.name,
'nolabel': '1',
'colspan': '2',
'attrs': "{'invisible':[('selection_" +
field.name + "','=','remove')]}"
})
elif field.ttype == "char": elif field.ttype == "char":
field_info = model_obj.fields_get(cr, uid, [field.name], context)
all_fields["selection_"+field.name] = {'type':'selection', 'string': field_info[field.name]['string'],'selection':[('set','Set'),('remove','Remove')]}
all_fields[field.name] = {'type':field.ttype, 'string': field.field_description, 'size': field.size or 256}
etree.SubElement(xml_group, 'field', {'name': "selection_"+field.name,'colspan':'2', 'colspan':'2'})
etree.SubElement(xml_group, 'field', {'name': field.name,'nolabel':'1', 'attrs':"{'invisible':[('selection_"+field.name+"','=','remove')]}", 'colspan':'2'})
field_info = model_obj.fields_get(
cr, uid, [field.name], context)
all_fields["selection_" + field.name] = {
'type': 'selection',
'string': field_info[field.name]['string'],
'selection': [
('set', 'Set'),
('remove', 'Remove')
]
}
all_fields[field.name] = {
'type': field.ttype,
'string': field.field_description,
'size': field.size or 256
}
etree.SubElement(
xml_group, 'field',
{
'name': "selection_" + field.name,
'colspan': '2',
})
etree.SubElement(
xml_group, 'field',
{
'name': field.name,
'nolabel': '1',
'attrs': "{'invisible':[('selection_" +
field.name + "','=','remove')]}",
'colspan': '2'
})
elif field.ttype == 'selection': elif field.ttype == 'selection':
field_info = model_obj.fields_get(cr, uid, [field.name], context)
all_fields["selection_"+field.name] = {'type':'selection', 'string': field_info[field.name]['string'],'selection':[('set','Set'),('remove','Remove')]}
field_info = model_obj.fields_get(cr, uid, [field.name], context)
etree.SubElement(xml_group, 'field', {'name': "selection_"+field.name, 'colspan':'2'})
etree.SubElement(xml_group, 'field', {'name': field.name,'nolabel':'1','colspan':'2', 'attrs':"{'invisible':[('selection_"+field.name+"','=','remove')]}"})
all_fields[field.name] = {'type':field.ttype, 'string': field.field_description, 'selection': field_info[field.name]['selection']}
field_info = model_obj.fields_get(
cr, uid, [field.name], context)
all_fields["selection_" + field.name] = {
'type': 'selection',
'string': field_info[field.name]['string'],
'selection': [
('set', 'Set'),
('remove', 'Remove')
]
}
field_info = model_obj.fields_get(
cr, uid, [field.name], context)
etree.SubElement(
xml_group, 'field',
{
'name': "selection_" + field.name,
'colspan': '2'
})
etree.SubElement(
xml_group, 'field',
{
'name': field.name,
'nolabel': '1',
'colspan': '2',
'attrs': "{'invisible':[('selection_" +
field.name + "','=','remove')]}"
})
all_fields[field.name] = {
'type': field.ttype,
'string': field.field_description,
'selection': field_info[field.name]['selection']
}
else: else:
field_info = model_obj.fields_get(cr, uid, [field.name], context)
all_fields[field.name] = {'type':field.ttype, 'string': field.field_description}
all_fields["selection_"+field.name] = {'type':'selection', 'string': field_info[field.name]['string'],'selection':[('set','Set'),('remove','Remove')]}
field_info = model_obj.fields_get(
cr, uid, [field.name], context)
all_fields[field.name] = {
'type': field.ttype,
'string': field.field_description
}
all_fields["selection_" + field.name] = {
'type': 'selection',
'string': field_info[field.name]['string'],
'selection': [
('set', 'Set'),
('remove', 'Remove')
]
}
if field.ttype == 'text': if field.ttype == 'text':
xml_group = etree.SubElement(xml_group, 'group', {'colspan': '6'})
etree.SubElement(xml_group, 'separator', {'string': all_fields[field.name]['string'],'colspan': '2'})
etree.SubElement(xml_group, 'field', {'name': "selection_"+field.name,'colspan': '2','nolabel':'1'})
etree.SubElement(xml_group, 'field', {'name': field.name, 'colspan':'4', 'nolabel':'1', 'attrs':"{'invisible':[('selection_"+field.name+"','=','remove')]}"})
xml_group = etree.SubElement(
xml_group, 'group', {'colspan': '6'})
etree.SubElement(
xml_group, 'separator',
{
'string': all_fields[field.name]['string'],
'colspan': '2'
})
etree.SubElement(
xml_group, 'field',
{
'name': "selection_" + field.name,
'colspan': '2',
'nolabel': '1',
})
etree.SubElement(
xml_group, 'field',
{
'name': field.name,
'colspan': '4',
'nolabel': '1',
'attrs': "{'invisible':[('selection_" +
field.name + "','=','remove')]}"
})
else: else:
all_fields["selection_"+field.name] = {'type':'selection', 'string': field_info[field.name]['string'],'selection':[('set','Set'),('remove','Remove')]}
etree.SubElement(xml_group, 'field', {'name': "selection_"+field.name, 'colspan': '2',})
etree.SubElement(xml_group, 'field', {'name': field.name,'nolabel':'1', 'attrs':"{'invisible':[('selection_"+field.name+"','=','remove')]}",'colspan': '2',})
all_fields["selection_" + field.name] = {
'type': 'selection',
'string': field_info[field.name]['string'],
'selection': [
('set', 'Set'),
('remove', 'Remove')
]
}
etree.SubElement(
xml_group, 'field',
{
'name': "selection_" + field.name,
'colspan': '2',
})
etree.SubElement(
xml_group, 'field',
{
'name': field.name,
'nolabel': '1',
'attrs': "{'invisible':[('selection_" +
field.name + "','=','remove')]}",
'colspan': '2',
})
etree.SubElement(xml_form, 'separator', {'string' : '','colspan': '6'})
xml_group3 = etree.SubElement(xml_form, 'group', {'col': '2', 'colspan': '4'})
etree.SubElement(xml_group3, 'button', {'string' :'Close','icon': "gtk-close", 'special' :'cancel'})
etree.SubElement(xml_group3, 'button', {'string' :'Apply','icon': "gtk-execute", 'type' :'object','name':"action_apply"})
etree.SubElement(
xml_form, 'separator', {'string': '', 'colspan': '6'})
xml_group3 = etree.SubElement(
xml_form, 'group', {'col': '2', 'colspan': '4'})
etree.SubElement(
xml_group3, 'button',
{
'string': 'Close',
'icon': "gtk-close",
'special': 'cancel'
})
etree.SubElement(
xml_group3, 'button',
{
'string': 'Apply',
'icon': "gtk-execute",
'type': 'object',
'name': "action_apply"
})
root = xml_form.getroottree() root = xml_form.getroottree()
result['arch'] = etree.tostring(root) result['arch'] = etree.tostring(root)
@ -117,12 +294,13 @@ class mass_editing_wizard(osv.osv_memory):
m2m_list.append((4, m2m_id)) m2m_list.append((4, m2m_id))
model_vals[model_field] = m2m_list model_vals[model_field] = m2m_list
if model_vals: if model_vals:
model_obj.write(cr, uid, context['active_ids'], model_vals, context)
model_obj.write(
cr, uid, context['active_ids'], model_vals, context)
result = super(mass_editing_wizard, self).create(cr, uid, {}, context) result = super(mass_editing_wizard, self).create(cr, uid, {}, context)
return result return result
def action_apply(self, cr, uid, ids, context=None): def action_apply(self, cr, uid, ids, context=None):
return {'type': 'ir.actions.act_window_close'}
return {'type': 'ir.actions.act_window_close'}
mass_editing_wizard() mass_editing_wizard()

6
security_protector/__openerp__.py

@ -9,9 +9,11 @@
'description': """ 'description': """
Prevent security to be changed when module is updated Prevent security to be changed when module is updated
This module overwrite ir model acces write delete function. This module overwrite ir model acces write delete function.
Only acces edited trough the UI or with manual_security_override in context set to True will be altered.
Only acces edited trough the UI or with manual_security_override in context
set to True will be altered.
When you try to delete a acces write it simply set all perms to false When you try to delete a acces write it simply set all perms to false
you can deactivate this behavior in ir.config_parameter by chanching the protect_security? key to 0
you can deactivate this behavior in ir.config_parameter by chanching the
protect_security? key to 0
""", """,
'author': 'Camptocamp', 'author': 'Camptocamp',
'website': 'http://openerp.camptocamp.com', 'website': 'http://openerp.camptocamp.com',

29
security_protector/security_protector.py

@ -3,7 +3,8 @@
# #
# Author Nicolas Bessi. Copyright Camptocamp SA # Author Nicolas Bessi. Copyright Camptocamp SA
############################################################################## ##############################################################################
from osv import fields, osv
from osv import osv
class IrModelAccess(osv.osv): class IrModelAccess(osv.osv):
"We inherit ir model access to add specific write unlink and copy behavior" "We inherit ir model access to add specific write unlink and copy behavior"
@ -12,35 +13,39 @@ class IrModelAccess(osv.osv):
def _acces_can_be_modified(self, cr, uid, context=None): def _acces_can_be_modified(self, cr, uid, context=None):
context = context or {} context = context or {}
on = self.pool.get('ir.config_parameter').get_param(cr, uid, 'protect_security?', default=False, context=context)
on = self.pool.get('ir.config_parameter').get_param(
cr, uid, 'protect_security?', default=False, context=context)
if on in (1, "1", "YES", True): if on in (1, "1", "YES", True):
if context.get('manual_security_override', False): if context.get('manual_security_override', False):
return True return True
return False return False
else: else:
return True return True
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
res =True
res = True
context = context or {} context = context or {}
if self._acces_can_be_modified(cr, uid, context=context): if self._acces_can_be_modified(cr, uid, context=context):
res = super(IrModelAccess, self).write(cr, uid, ids, vals, context=context)
res = super(IrModelAccess, self).write(
cr, uid, ids, vals, context=context)
return res return res
def unlink(self, cr, uid, ids, context=None): def unlink(self, cr, uid, ids, context=None):
res = True res = True
context = context or {} context = context or {}
# I'm note sur about this one maybe we should do nothing # I'm note sur about this one maybe we should do nothing
if self._acces_can_be_modified(cr, uid, context=context): if self._acces_can_be_modified(cr, uid, context=context):
vals = {'perm_read':False,
'perm_write': False,
'perm_unlink': False,
'perm_create': False}
super(IrModelAccess, self).write(cr, uid, ids, vals, context=context)
vals = {
'perm_read': False,
'perm_write': False,
'perm_unlink': False,
'perm_create': False
}
super(IrModelAccess, self).write(
cr, uid, ids, vals, context=context)
else: else:
res = super(IrModelAccess, self).unlink(cr, uid, ids, context=context)
res = super(IrModelAccess, self).unlink(
cr, uid, ids, context=context)
return res return res

3
server_env_base_external_referentials/__init__.py

@ -18,5 +18,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import base_external_referentials
from . import base_external_referentials

11
server_env_base_external_referentials/__openerp__.py

@ -25,10 +25,13 @@
"depends": ["base", 'server_environment', 'base_external_referentials'], "depends": ["base", 'server_environment', 'base_external_referentials'],
"author": "Camptocamp", "author": "Camptocamp",
'license': 'AGPL-3', 'license': 'AGPL-3',
"description": """This module is based on the server_environment module to use files for configuration.
Thus we can have a different file for each environment (dev, test, staging, prod).
This module define the config variables for the base_external_referential module.
In the configuration file, you can configure the url, login and password of the referentials
"description": """This module is based on the server_environment module to
use files for configuration. Thus we can have a different file for each
environment (dev, test, staging, prod).
This module define the config variables for the base_external_referential
module.
In the configuration file, you can configure the url, login and password of the
referentials.
Exemple of the section to put in the configuration file : Exemple of the section to put in the configuration file :

26
server_env_base_external_referentials/base_external_referentials.py

@ -24,31 +24,37 @@ from server_environment import serv_config
import logging import logging
class external_referential(osv.osv): class external_referential(osv.osv):
_inherit = 'external.referential' _inherit = 'external.referential'
def _get_environment_config_by_name(self, cr, uid, ids, field_names, arg, context):
def _get_environment_config_by_name(self, cr, uid, ids, field_names, arg,
context):
values = {} values = {}
for referential in self.browse(cr, uid, ids, context): for referential in self.browse(cr, uid, ids, context):
values[referential.id] = {} values[referential.id] = {}
for field_name in field_names: for field_name in field_names:
section_name = '.'.join((self._name.replace('.', '_'), referential.name))
section_name = '.'.join((self._name.replace('.', '_'),
referential.name))
try: try:
value = serv_config.get(section_name, field_name) value = serv_config.get(section_name, field_name)
values[referential.id].update({field_name: value}) values[referential.id].update({field_name: value})
except: except:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.exception('error trying to read field %s in section %s', field_name, section_name)
logger.exception(
'error trying to read field %s in section %s',
field_name, section_name)
return values return values
_columns = { _columns = {
'location': fields.function(_get_environment_config_by_name, type='char', size=200,
method=True, string='Location', multi='connection_config'),
'apiusername': fields.function(_get_environment_config_by_name, type='char', size=64,
method=True, string='User Name', multi='connection_config'),
'apipass': fields.function(_get_environment_config_by_name, type='char', size=64,
method=True, string='Password', multi='connection_config'),
'location': fields.function(
_get_environment_config_by_name, type='char', size=200,
method=True, string='Location', multi='connection_config'),
'apiusername': fields.function(
_get_environment_config_by_name, type='char', size=64,
method=True, string='User Name', multi='connection_config'),
'apipass': fields.function(
_get_environment_config_by_name, type='char', size=64,
method=True, string='Password', multi='connection_config'),
} }
external_referential() external_referential()

14
server_environment/serv_config.py

@ -23,7 +23,7 @@ import os
import ConfigParser import ConfigParser
from lxml import etree from lxml import etree
from openerp.osv import osv, fields, orm
from openerp.osv import fields, orm
from openerp.tools.config import config as system_base_config from openerp.tools.config import config as system_base_config
from .system_info import get_server_environment from .system_info import get_server_environment
@ -46,14 +46,17 @@ if not system_base_config.get('running_env', False):
ck_path = os.path.join(_dir, system_base_config['running_env']) ck_path = os.path.join(_dir, system_base_config['running_env'])
if not os.path.exists(ck_path) :
if not os.path.exists(ck_path):
raise Exception( raise Exception(
"Provided server environment does not exist, " "Provided server environment does not exist, "
"please add a folder %s" % ck_path "please add a folder %s" % ck_path
) )
def setboolean(obj, attr, _bool=_boolean_states):
def setboolean(obj, attr, _bool=None):
"""Replace the attribute with a boolean.""" """Replace the attribute with a boolean."""
if _bool is None:
_bool = _boolean_states
res = _bool[getattr(obj, attr).lower()] res = _bool[getattr(obj, attr).lower()]
setattr(obj, attr, res) setattr(obj, attr, res)
return res return res
@ -115,7 +118,7 @@ class ServerConfiguration(orm.TransientModel):
_conf_defaults = _Defaults() _conf_defaults = _Defaults()
def __init__(self, pool, cr): def __init__(self, pool, cr):
res = super(ServerConfiguration, self).__init__(pool, cr)
super(ServerConfiguration, self).__init__(pool, cr)
self.running_env = system_base_config['running_env'] self.running_env = system_base_config['running_env']
# Only show passwords in development # Only show passwords in development
self.show_passwords = self.running_env in ('dev',) self.show_passwords = self.running_env in ('dev',)
@ -170,7 +173,7 @@ class ServerConfiguration(orm.TransientModel):
self._arch = etree.fromstring(arch) self._arch = etree.fromstring(arch)
def fields_view_get(self, cr, uid, view_id=None, view_type='form', def fields_view_get(self, cr, uid, view_id=None, view_type='form',
context=None, toolbar=False, submenu=False):
context=None, toolbar=False, submenu=False):
"""Overwrite the default method to render the custom view.""" """Overwrite the default method to render the custom view."""
res = super(ServerConfiguration, self).fields_view_get(cr, uid, res = super(ServerConfiguration, self).fields_view_get(cr, uid,
view_id, view_id,
@ -187,7 +190,6 @@ class ServerConfiguration(orm.TransientModel):
res['fields'] = xfields res['fields'] = xfields
return res return res
def default_get(self, cr, uid, fields_list, context=None): def default_get(self, cr, uid, fields_list, context=None):
res = {} res = {}
for key in self._conf_defaults: for key in self._conf_defaults:

2
super_calendar/__init__.py

@ -18,4 +18,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import super_calendar
from . import super_calendar

37
super_calendar/__openerp__.py

@ -26,9 +26,13 @@
'description': """ 'description': """
This module allows to create configurable calendars. This module allows to create configurable calendars.
Through the 'calendar configurator' object, you can specify which models have to be merged in the super calendar. For each model, you have to define the 'description' and 'date_start' fields at least. Then you can define 'duration' and the 'user_id' fields.
Through the 'calendar configurator' object, you can specify which models have
to be merged in the super calendar. For each model, you have to define the
'description' and 'date_start' fields at least. Then you can define 'duration'
and the 'user_id' fields.
The 'super.calendar' object contains the the merged calendars. The 'super.calendar' can be updated by 'ir.cron' or manually.
The 'super.calendar' object contains the the merged calendars. The
'super.calendar' can be updated by 'ir.cron' or manually.
Configuration Configuration
============= =============
@ -37,39 +41,48 @@ After installing the module you can go to
Super calendar Configuration Configurators Super calendar Configuration Configurators
and create a new configurator. For instance, if you want to see meetings and phone calls, you can create the following lines
and create a new configurator. For instance, if you want to see meetings and
phone calls, you can create the following lines
.. image:: http://planet.domsense.com/wp-content/uploads/2012/04/meetings.png .. image:: http://planet.domsense.com/wp-content/uploads/2012/04/meetings.png
:width: 400 px :width: 400 px
.. image:: http://planet.domsense.com/wp-content/uploads/2012/04/phone_calls.png
.. image::
http://planet.domsense.com/wp-content/uploads/2012/04/phone_calls.png
:width: 400 px :width: 400 px
Then, you can use the Generate Calendar button or wait for the scheduled action (Generate Calendar Records) to be run.
Then, you can use the Generate Calendar button or wait for the scheduled
action (Generate Calendar Records) to be run.
When the calendar is generated, you can visualize it by the super calendar main menu.
When the calendar is generated, you can visualize it by the super calendar
main menu.
Here is a sample monthly calendar: Here is a sample monthly calendar:
.. image:: http://planet.domsense.com/wp-content/uploads/2012/04/month_calendar.png
.. image::
http://planet.domsense.com/wp-content/uploads/2012/04/month_calendar.png
:width: 400 px :width: 400 px
And here is the weekly one: And here is the weekly one:
.. image:: http://planet.domsense.com/wp-content/uploads/2012/04/week_calendar.png
.. image::
http://planet.domsense.com/wp-content/uploads/2012/04/week_calendar.png
:width: 400 px :width: 400 px
As you can see, several filters are available. A typical usage consists in filtering by Configurator (if you have several configurators, Scheduled calls and meetings can be one of them) and by your user. Once you filtered, you can save the filter as Advanced filter or even add it to a dashboard.
As you can see, several filters are available. A typical usage consists in
filtering by Configurator (if you have several configurators, Scheduled
calls and meetings can be one of them) and by your user. Once you filtered,
you can save the filter as Advanced filter or even add it to a dashboard.
""", """,
'author': 'Agile Business Group', 'author': 'Agile Business Group',
'website': 'http://www.agilebg.com', 'website': 'http://www.agilebg.com',
'license': 'AGPL-3', 'license': 'AGPL-3',
'depends' : ['base'],
"data" : [
'depends': ['base'],
"data": [
'super_calendar_view.xml', 'super_calendar_view.xml',
'cron_data.xml', 'cron_data.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
],
],
'demo': [], 'demo': [],
'test': [], 'test': [],
'installable': True, 'installable': True,

137
super_calendar/super_calendar.py

@ -27,106 +27,151 @@ from datetime import datetime
from openerp import tools from openerp import tools
from openerp.tools import safe_eval as eval from openerp.tools import safe_eval as eval
def _models_get(self, cr, uid, context=None): def _models_get(self, cr, uid, context=None):
obj = self.pool.get('ir.model') obj = self.pool.get('ir.model')
ids = obj.search(cr, uid, []) ids = obj.search(cr, uid, [])
res = obj.read(cr, uid, ids, ['model', 'name'], context) res = obj.read(cr, uid, ids, ['model', 'name'], context)
return [(r['model'], r['name']) for r in res] return [(r['model'], r['name']) for r in res]
class super_calendar_configurator(orm.Model): class super_calendar_configurator(orm.Model):
_logger = logging.getLogger('super.calendar') _logger = logging.getLogger('super.calendar')
_name = 'super.calendar.configurator' _name = 'super.calendar.configurator'
_columns = {
_columns = {
'name': fields.char('Name', size=64, required=True), 'name': fields.char('Name', size=64, required=True),
'line_ids': fields.one2many('super.calendar.configurator.line', 'configurator_id', 'Lines'),
}
'line_ids': fields.one2many(
'super.calendar.configurator.line', 'configurator_id', 'Lines'),
}
def generate_calendar_records(self, cr, uid, ids, context=None): def generate_calendar_records(self, cr, uid, ids, context=None):
configurator_ids = self.search(cr, uid, []) configurator_ids = self.search(cr, uid, [])
super_calendar_pool = self.pool.get('super.calendar') super_calendar_pool = self.pool.get('super.calendar')
# removing old records # removing old records
super_calendar_ids = super_calendar_pool.search(cr, uid, [], context=context)
super_calendar_pool.unlink(cr, uid, super_calendar_ids, context=context)
super_calendar_ids = super_calendar_pool.search(
cr, uid, [], context=context)
super_calendar_pool.unlink(
cr, uid, super_calendar_ids, context=context)
for configurator in self.browse(cr, uid, configurator_ids, context): for configurator in self.browse(cr, uid, configurator_ids, context):
for line in configurator.line_ids: for line in configurator.line_ids:
current_pool = self.pool.get(line.name.model) current_pool = self.pool.get(line.name.model)
current_record_ids = current_pool.search(cr, uid, line.domain and eval(line.domain) or [], context=context)
current_record_ids = current_pool.search(
cr, uid, line.domain and eval(line.domain) or [],
context=context)
for current_record_id in current_record_ids: for current_record_id in current_record_ids:
current_record = current_pool.browse(cr, uid, current_record_id, context=context)
current_record = current_pool.browse(
cr, uid, current_record_id, context=context)
if line.user_field_id and \ if line.user_field_id and \
current_record[line.user_field_id.name] and current_record[line.user_field_id.name]._table_name != 'res.users':
raise osv.except_osv(_('Error'),
_("The 'User' field of record %s (%s) does not refer to res.users")
% (current_record[line.description_field_id.name], line.name.model))
current_record[line.user_field_id.name] and\
current_record[line.user_field_id.name]._table_name \
!= 'res.users':
raise osv.except_osv(
_('Error'),
_("The 'User' field of record %s (%s) does not "
"refer to res.users")
% (
current_record[line.description_field_id.name],
line.name.model)
)
if (((line.description_field_id if (((line.description_field_id
and current_record[line.description_field_id.name])
or line.description_code)
and current_record[line.date_start_field_id.name]):
and current_record[line.description_field_id.name])
or line.description_code)
and current_record[line.date_start_field_id.name]):
duration = False duration = False
if not line.duration_field_id and line.date_stop_field_id and current_record[line.date_start_field_id.name] and current_record[line.date_stop_field_id.name]:
date_start= datetime.strptime(current_record[line.date_start_field_id.name], tools.DEFAULT_SERVER_DATETIME_FORMAT)
date_stop= datetime.strptime(current_record[line.date_stop_field_id.name], tools.DEFAULT_SERVER_DATETIME_FORMAT)
duration = (date_stop - date_start).total_seconds() / 3600
if not line.duration_field_id and\
line.date_stop_field_id and\
current_record[line.date_start_field_id.name] and\
current_record[line.date_stop_field_id.name]:
date_start = datetime.strptime(
current_record[line.date_start_field_id.name],
tools.DEFAULT_SERVER_DATETIME_FORMAT)
date_stop = datetime.strptime(
current_record[line.date_stop_field_id.name],
tools.DEFAULT_SERVER_DATETIME_FORMAT)
duration = (date_stop - date_start).total_seconds()
duration /= 3600
elif line.duration_field_id: elif line.duration_field_id:
duration = current_record[line.duration_field_id.name]
duration = current_record[
line.duration_field_id.name]
if line.description_type != 'code': if line.description_type != 'code':
name = current_record[line.description_field_id.name]
name = current_record[
line.description_field_id.name]
else: else:
parse_dict = {'o': current_record} parse_dict = {'o': current_record}
mytemplate = Template(line.description_code) mytemplate = Template(line.description_code)
name= mytemplate.render(**parse_dict)
name = mytemplate.render(**parse_dict)
super_calendar_values = { super_calendar_values = {
'name': name, 'name': name,
'model_description': line.description, 'model_description': line.description,
'date_start': current_record[line.date_start_field_id.name],
'date_start': current_record[
line.date_start_field_id.name],
'duration': duration, 'duration': duration,
'user_id': line.user_field_id and current_record[line.user_field_id.name] and current_record[line.user_field_id.name].id or False,
'user_id': line.user_field_id and
current_record[line.user_field_id.name] and
current_record[line.user_field_id.name].id or
False,
'configurator_id': configurator.id, 'configurator_id': configurator.id,
'res_id': line.name.model+','+str(current_record['id']),
'res_id': line.name.model + ',' +
str(current_record['id']),
'model_id': line.name.id, 'model_id': line.name.id,
}
super_calendar_pool.create(cr, uid, super_calendar_values, context=context)
}
super_calendar_pool.create(
cr, uid, super_calendar_values, context=context)
self._logger.info('Calendar generated') self._logger.info('Calendar generated')
return True return True
class super_calendar_configurator_line(orm.Model): class super_calendar_configurator_line(orm.Model):
_name = 'super.calendar.configurator.line' _name = 'super.calendar.configurator.line'
_columns = {
_columns = {
'name': fields.many2one('ir.model', 'Model', required=True), 'name': fields.many2one('ir.model', 'Model', required=True),
'description': fields.char('Description', size=128, required=True), 'description': fields.char('Description', size=128, required=True),
'domain': fields.char('Domain', size=512), 'domain': fields.char('Domain', size=512),
'configurator_id': fields.many2one('super.calendar.configurator', 'Configurator'),
'description_type': fields.selection([
('field', 'Field'),
('code', 'Code'),
'configurator_id': fields.many2one(
'super.calendar.configurator', 'Configurator'),
'description_type': fields.selection(
[
('field', 'Field'),
('code', 'Code'),
], string="Description Type"), ], string="Description Type"),
'description_field_id': fields.many2one('ir.model.fields', 'Description field',
'description_field_id': fields.many2one(
'ir.model.fields', 'Description field',
domain="[('model_id', '=', name),('ttype', '=', 'char')]"), domain="[('model_id', '=', name),('ttype', '=', 'char')]"),
'description_code': fields.text('Description field', help="Use '${o}' to refer to the involved object. E.g.: '${o.project_id.name}'"),
'date_start_field_id': fields.many2one('ir.model.fields', 'Start date field',
domain="['&','|',('ttype', '=', 'datetime'),('ttype', '=', 'date'),('model_id', '=', name)]",
'description_code': fields.text(
'Description field',
help="Use '${o}' to refer to the involved object. "
"E.g.: '${o.project_id.name}'"),
'date_start_field_id': fields.many2one(
'ir.model.fields', 'Start date field',
domain="['&','|',('ttype', '=', 'datetime'),"
"('ttype', '=', 'date'),('model_id', '=', name)]",
required=True), required=True),
'date_stop_field_id': fields.many2one('ir.model.fields', 'End date field',
'date_stop_field_id': fields.many2one(
'ir.model.fields', 'End date field',
domain="['&',('ttype', '=', 'datetime'),('model_id', '=', name)]"), domain="['&',('ttype', '=', 'datetime'),('model_id', '=', name)]"),
'duration_field_id': fields.many2one('ir.model.fields', 'Duration field',
'duration_field_id': fields.many2one(
'ir.model.fields', 'Duration field',
domain="['&',('ttype', '=', 'float'),('model_id', '=', name)]"), domain="['&',('ttype', '=', 'float'),('model_id', '=', name)]"),
'user_field_id': fields.many2one('ir.model.fields', 'User field',
'user_field_id': fields.many2one(
'ir.model.fields', 'User field',
domain="['&',('ttype', '=', 'many2one'),('model_id', '=', name)]"), domain="['&',('ttype', '=', 'many2one'),('model_id', '=', name)]"),
}
}
class super_calendar(orm.Model): class super_calendar(orm.Model):
_name = 'super.calendar' _name = 'super.calendar'
_columns = {
_columns = {
'name': fields.char('Description', size=512, required=True), 'name': fields.char('Description', size=512, required=True),
'model_description': fields.char('Model Description', size=128, required=True),
'date_start':fields.datetime('Start date', required=True),
'duration':fields.float('Duration'),
'model_description': fields.char(
'Model Description', size=128, required=True),
'date_start': fields.datetime('Start date', required=True),
'duration': fields.float('Duration'),
'user_id': fields.many2one('res.users', 'User'), 'user_id': fields.many2one('res.users', 'User'),
'configurator_id': fields.many2one('super.calendar.configurator', 'Configurator'),
'res_id': fields.reference('Resource', selection=_models_get, size=128),
'configurator_id': fields.many2one(
'super.calendar.configurator', 'Configurator'),
'res_id': fields.reference(
'Resource', selection=_models_get, size=128),
'model_id': fields.many2one('ir.model', 'Model'), 'model_id': fields.many2one('ir.model', 'Model'),
}
}

2
unserialize_field/__init__.py

@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import ir_model_fields
from . import ir_model_fields

31
unserialize_field/ir_model_fields.py

@ -44,15 +44,17 @@ class ir_model_fields(orm.Model):
ids = pool_obj.search( ids = pool_obj.search(
cr, uid, cr, uid,
[(this.serialization_field_id.name, '!=', '{}')], [(this.serialization_field_id.name, '!=', '{}')],
offset=offset*step, limit=step, context=context)
offset=offset * step, limit=step, context=context)
if not ids: if not ids:
break break
for data in pool_obj.read(cr, uid, ids,
[this.serialization_field_id.name],
context=context):
self.unserialize_field(cr, uid, pool_obj, data,
this.serialization_field_id.name,
this.name, context=context)
for data in pool_obj.read(
cr, uid, ids,
[this.serialization_field_id.name],
context=context):
self.unserialize_field(
cr, uid, pool_obj, data,
this.serialization_field_id.name,
this.name, context=context)
offset += 1 offset += 1
finally: finally:
cr.commit = commit_org cr.commit = commit_org
@ -98,7 +100,7 @@ class ir_model_fields(orm.Model):
serialization_field_name, field_name, serialization_field_name, field_name,
context=None): context=None):
serialized_values = read_record[serialization_field_name] serialized_values = read_record[serialization_field_name]
if not field_name in serialized_values:
if field_name not in serialized_values:
return False return False
value = serialized_values.pop(field_name) value = serialized_values.pop(field_name)
@ -106,10 +108,9 @@ class ir_model_fields(orm.Model):
value = [(6, 0, value)] value = [(6, 0, value)]
return pool_obj.write( return pool_obj.write(
cr, uid, read_record['id'],
{
field_name: value,
serialization_field_name: serialized_values,
},
context=context)
cr, uid, read_record['id'],
{
field_name: value,
serialization_field_name: serialized_values,
},
context=context)

2
view_groups_id/__init__.py

@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import model
from . import model

9
view_groups_id/__openerp__.py

@ -19,7 +19,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
{ {
'name': 'group_ids for ir.ui.view', 'name': 'group_ids for ir.ui.view',
'version': '1.0', 'version': '1.0',
@ -39,14 +38,10 @@
"category": "Dependency", "category": "Dependency",
"depends": [ "depends": [
'base', 'base',
],
'css': [
],
],
'data': [ 'data': [
'view/ir_ui_view.xml', 'view/ir_ui_view.xml',
],
'js': [
],
],
'installable': True, 'installable': True,
'active': False, 'active': False,
'certificate': '', 'certificate': '',

2
view_groups_id/model/__init__.py

@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import ir_ui_view
from . import ir_ui_view

28
view_groups_id/model/ir_ui_view.py

@ -33,23 +33,25 @@ class ir_ui_view(Model):
_inherit = 'ir.ui.view' _inherit = 'ir.ui.view'
_columns = { _columns = {
'groups_id': fields.many2many(
'res.groups', 'ir_ui_view_group_rel', 'view_id', 'group_id',
string='Groups', help="If this field is empty, the view "
"applies to all users. Otherwise, the view applies to the "
"users of those groups only."),
}
'groups_id': fields.many2many(
'res.groups', 'ir_ui_view_group_rel', 'view_id', 'group_id',
string='Groups', help="If this field is empty, the view "
"applies to all users. Otherwise, the view applies to the "
"users of those groups only."),
}
def get_inheriting_views_arch(self, cr, uid, view_id, model, context=None): def get_inheriting_views_arch(self, cr, uid, view_id, model, context=None):
user_groups = frozenset(self.pool.get('res.users').browse( user_groups = frozenset(self.pool.get('res.users').browse(
cr, SUPERUSER_ID, uid, context).groups_id) cr, SUPERUSER_ID, uid, context).groups_id)
view_ids = [v[1] for v in
super(ir_ui_view, self).get_inheriting_views_arch(
cr, uid, view_id, model, context=context)]
view_ids = [
v[1] for v in super(ir_ui_view, self).get_inheriting_views_arch(
cr, uid, view_id, model, context=context)
]
# filter views based on user groups # filter views based on user groups
return [(view.arch, view.id)
for view in self.browse(cr, SUPERUSER_ID, view_ids, context)
if not (view.groups_id and
user_groups.isdisjoint(view.groups_id))]
return [
(view.arch, view.id)
for view in self.browse(cr, SUPERUSER_ID, view_ids, context)
if not (view.groups_id and user_groups.isdisjoint(view.groups_id))
]
Loading…
Cancel
Save