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. 18
      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. 13
      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. 110
      fetchmail_attach_from_folder/wizard/attach_mail_manually.py
  20. 2
      mail_client_view/__init__.py
  21. 4
      mail_client_view/__openerp__.py
  22. 2
      mail_client_view/model/__init__.py
  23. 13
      mail_client_view/model/mail_message.py
  24. 13
      mail_environment/__openerp__.py
  25. 78
      mail_environment/env_mail.py
  26. 8
      mass_editing/__init__.py
  27. 23
      mass_editing/__openerp__.py
  28. 95
      mass_editing/mass_editing.py
  29. 7
      mass_editing/wizard/__init__.py
  30. 270
      mass_editing/wizard/mass_editing_wizard.py
  31. 2
      security_protector/__init__.py
  32. 6
      security_protector/__openerp__.py
  33. 41
      security_protector/security_protector.py
  34. 3
      server_env_base_external_referentials/__init__.py
  35. 11
      server_env_base_external_referentials/__openerp__.py
  36. 26
      server_env_base_external_referentials/base_external_referentials.py
  37. 14
      server_environment/serv_config.py
  38. 2
      server_environment_files/__init__.py
  39. 4
      super_calendar/__init__.py
  40. 39
      super_calendar/__openerp__.py
  41. 145
      super_calendar/super_calendar.py
  42. 2
      unserialize_field/__init__.py
  43. 41
      unserialize_field/ir_model_fields.py
  44. 2
      view_groups_id/__init__.py
  45. 11
      view_groups_id/__openerp__.py
  46. 2
      view_groups_id/model/__init__.py
  47. 28
      view_groups_id/model/ir_ui_view.py

3
.travis.yml

@ -4,8 +4,7 @@ python:
- "2.7"
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:
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
@jsonrequest
def get_list(self, req):
db_filter = req.httprequest.environ.get('HTTP_X_OPENERP_DBFILTER', '.*')
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

18
dbfilter_from_header/__openerp__.py

@ -19,14 +19,14 @@
#
##############################################################################
{
"name" : "dbfilter_from_header",
"version" : "1.0",
"author" : "Therp BV",
"name": "dbfilter_from_header",
"version": "1.0",
"author": "Therp BV",
"complexity": "normal",
"description": """
This addon lets you pass a dbfilter as a HTTP header.
This is interesting for setups where database names can't be mapped to
This is interesting for setups where database names can't be mapped to
proxied host names.
In nginx, use
@ -34,11 +34,11 @@
The addon has to be loaded as server-wide module.
""",
"category" : "Tools",
"depends" : [
"category": "Tools",
"depends": [
'web',
],
"data" : [
"data": [
],
"js": [
],
@ -46,7 +46,7 @@
],
"auto_install": False,
"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",
"description": """If an organisation's email layout is a bit more
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:
@ -46,7 +46,8 @@ For example, create a template template
-----
Example Corp logo
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 street 42
Example city
@ -58,7 +59,8 @@ Then in your template you write
-----
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
@ -75,12 +77,17 @@ Example city
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:
------
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}
Best,
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/>.
#
##############################################################################
import email_template
from . import email_template

13
email_template_template/model/email_template.py

@ -27,7 +27,7 @@ class email_template(Model):
def _get_is_template_template(self, cr, uid, ids, fields_name, arg,
context=None):
cr.execute('''select
cr.execute('''select
id, (select count(*) > 0 from email_template e
where email_template_id=email_template.id)
from email_template
@ -39,18 +39,19 @@ class email_template(Model):
'is_template_template': fields.function(
_get_is_template_template, type='boolean',
string='Is a template template'),
}
}
def get_email_template(self, cr, uid, template_id=False, record_id=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)
if this.email_template_id and not this.is_template_template:
for field in ['body_html', 'body_text']:
if this[field] and this.email_template_id[field]:
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

7
fetchmail_attach_from_folder/__init__.py

@ -19,7 +19,6 @@
# 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',
'version': '1.0.1',
'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
client integration.
@ -38,7 +39,7 @@
'view/fetchmail_server.xml',
'wizard/attach_mail_manually.xml',
'security/ir.model.access.csv',
],
],
'js': [],
'installable': True,
'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/>.
#
##############################################################################
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):
name = None
'''Name shown to the user'''
'Name shown to the user'
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 = []
'''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):
'''Returns ids found for model with mail_message'''
@ -39,5 +39,5 @@ class base(object):
self, cr, uid, connection, object_id, folder,
mail_message, mail_message_org, msgid, context=None):
'''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/>.
#
##############################################################################
from .email_exact import email_exact
from email_exact import email_exact
class email_domain(email_exact):
'''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):
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:
domains = []
for addr in self._get_mailaddresses(conf, mail_message):
domains.append(addr.split('@')[-1])
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

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.addons.mail.mail_message import to_email
class email_exact(base):
'''Search for exactly the mailadress as noted in the email'''
@ -36,17 +37,17 @@ class email_exact(base):
for field in fields:
if field in mail_message:
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(
self, conf, mail_message, operator='=', values=None):
mailaddresses = values or self._get_mailaddresses(
conf, mail_message)
conf, mail_message)
if not mailaddresses:
return [(0, '=', 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
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/>.
#
##############################################################################
from .base import base
from base import base
from openerp.tools.safe_eval import safe_eval
class openerp_standard(base):
'''No search at all. Use OpenERP's standard mechanism to attach mails to
mail.thread objects. Note that this algorithm always matches.'''
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):
'''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,
mail_message, mail_message_org, msgid, context):
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:
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/>.
#
##############################################################################
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 simplejson
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.osv import fields
from openerp.addons.fetchmail.fetchmail import logger
from openerp.tools.misc import UnquoteEvalContext
from openerp.tools.safe_eval import safe_eval
class fetchmail_server(Model):
@ -36,7 +35,7 @@ class fetchmail_server(Model):
_columns = {
'folder_ids': fields.one2many(
'fetchmail.server.folder', 'server_id', 'Folders'),
'fetchmail.server.folder', 'server_id', 'Folders'),
}
_defaults = {
@ -45,7 +44,7 @@ class fetchmail_server(Model):
def __init__(self, pool, cr):
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(
self, cr, uid, ids, server_type=False, ssl=False,
@ -96,22 +95,23 @@ class fetchmail_server(Model):
if connection.select(folder.path)[0] != 'OK':
logger.error(
'Could not open mailbox %s on %s' % (
folder.path, this.server))
folder.path, this.server))
connection.select()
continue
result, msgids = this.get_msgids(connection)
if result != 'OK':
logger.error(
'Could not search mailbox %s on %s' % (
folder.path, this.server))
folder.path, this.server))
continue
for msgid in msgids[0].split():
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
@ -130,15 +130,16 @@ class fetchmail_server(Model):
if result != 'OK':
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
mail_message = self.pool.get('mail.message').parse_message(
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
found_ids = match_algorithm.search_matches(
@ -155,7 +156,7 @@ class fetchmail_server(Model):
msgdata[0][1], msgid, context)
cr.execute('release savepoint apply_matching')
matched_object_ids += found_ids[:1]
except Exception, e:
except Exception:
cr.execute('rollback to savepoint apply_matching')
logger.exception(
"Failed to fetch mail %s from %s",
@ -180,48 +181,48 @@ class fetchmail_server(Model):
partner_id = self.pool.get(
folder.model_id.model).browse(
cr, uid, object_id, context
).partner_id.id
).partner_id.id
attachments=[]
attachments = []
if this.attach and mail_message.get('attachments'):
for attachment in mail_message['attachments']:
fname, fcontent = attachment
if isinstance(fcontent, unicode):
fcontent = fcontent.encode('utf-8')
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(
self.pool.get('ir.attachment').create(
cr, uid, data_attach, context=context))
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:
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_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(
match_algorithm.base.base)])
@ -47,55 +48,55 @@ class fetchmail_server_folder(Model):
_columns = {
'sequence': fields.integer('Sequence'),
'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(
'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(
'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(
'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(
_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(
'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'),
'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',
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(
'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', 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(
[
('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 = {

3
fetchmail_attach_from_folder/wizard/__init__.py

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

110
fetchmail_attach_from_folder/wizard/attach_mail_manually.py

@ -20,6 +20,7 @@
#
##############################################################################
import logging
from openerp.osv import fields
from openerp.osv.orm import TransientModel
@ -28,46 +29,53 @@ class attach_mail_manually(TransientModel):
_name = 'fetchmail.attach.mail.manually'
_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):
if context is None:
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.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':
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
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():
result, msgdata = connection.fetch(msgid, '(RFC822)')
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
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()
return defaults
@ -77,34 +85,40 @@ class attach_mail_manually(TransientModel):
for mail in this.mail_ids:
connection = this.folder_id.server_id.connect()
connection.select(this.folder_id.path)
result, msgdata = connection.fetch(mail.msgid, '(RFC822)')
if result != 'OK':
logger.error('Could not fetch %s in %s on %s' % (
msgid, folder.path, this.server))
continue
mail_message = self.pool.get('mail.message').parse_message(
msgdata[0][1], this.folder_id.server_id.original)
result, msgdata = connection.fetch(mail.msgid, '(RFC822)')
if result != 'OK':
logging.error(
'Could not fetch %s in %s on %s',
mail.msgid, this.folder_id.path,
this.folder_id.server_id.server)
continue
this.folder_id.server_id.attach_mail(connection,
mail.object_id.id, this.folder_id, mail_message,
mail.msgid)
mail_message = self.pool.get('mail.message').parse_message(
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)
connection.close()
return {'type': 'ir.actions.act_window_close'}
class attach_mail_manually_mail(TransientModel):
_name = 'fetchmail.attach.mail.manually.mail'
_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/>.
#
##############################################################################
import model
from . import model

4
mail_client_view/__openerp__.py

@ -27,7 +27,7 @@
Adds a menu to the customer address book that enables ordinary users to
look at customer or other mail. Also adds an 'action needed' boolean to
mail messages, to quickly select all mails that still have to be acted on.
The action_needed flag will be shown to users in a tree view as a red
circle, no action needed will be green. In a form users either have the
button 'confirm action done' (if action needed), or the button 'set
@ -40,7 +40,7 @@
'data': [
'view/mail_user_menu.xml',
'view/mail_user_view.xml',
],
],
'js': [],
'installable': True,
'active': False,

2
mail_client_view/model/__init__.py

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

13
mail_client_view/model/mail_message.py

@ -13,7 +13,7 @@ from openerp.osv import fields
class mail_message(Model):
'''Extend mail_message with action_needed flag'''
_inherit = 'mail.message'
def set_action_needed_off(self, cr, user, ids, context=None):
self.write(cr, user, ids, {'action_needed': False}, context=context)
return True
@ -21,19 +21,20 @@ class mail_message(Model):
def set_action_needed_on(self, cr, user, ids, context=None):
self.write(cr, user, ids, {'action_needed': True}, context=context)
return True
def create(self, cr, user, vals, context=None):
# Set newly received messages as needing action, unless an
# 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
mm_id = super(mail_message, self).create(
cr, user, vals, context=context)
return mm_id
_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'
' when a user flags a message as needing attention.'),
}

13
mail_environment/__openerp__.py

@ -26,15 +26,17 @@
'description': """
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 :
[outgoing_mail]
smtp_host = smtp.myserver.com
smtp_port = 587
smtp_user =
smtp_user =
smtp_pass =
smtp_encryption = ssl
@ -57,7 +59,10 @@ password = openerp
'author': 'Camptocamp',
'license': 'AGPL-3',
'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': [],
'update_xml': ['mail_view.xml'],
'demo_xml': [],

78
mail_environment/env_mail.py

@ -27,7 +27,7 @@ from server_environment import serv_config
class IrMail(osv.osv):
_inherit = "ir.mail_server"
def _get_smtp_conf(self, cursor, uid, ids, name, args, context=None):
"""
Return configuration
@ -41,7 +41,8 @@ class IrMail(osv.osv):
if serv_config.has_section(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):
config_vals.update(serv_config.items(custom_section_name))
@ -52,7 +53,7 @@ class IrMail(osv.osv):
return res
_columns = {
'smtp_host': fields.function(_get_smtp_conf,
'smtp_host': fields.function(_get_smtp_conf,
method=True,
string='SMTP Server',
type="char",
@ -63,33 +64,40 @@ class IrMail(osv.osv):
string='SMTP Port',
type="integer",
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),
'smtp_user': fields.function(_get_smtp_conf,
method=True,
string='Username',
type="char",
multi='outgoing_mail_config',
help="Optional username for SMTP authentication",
help="Optional username for SMTP "
"authentication",
size=64),
'smtp_pass': fields.function(_get_smtp_conf,
method=True,
string='Password',
type="char",
multi='outgoing_mail_config',
help="Optional password for SMTP authentication",
help="Optional password for SMTP "
"authentication",
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()
@ -105,10 +113,12 @@ class FetchmailServer(osv.osv):
for fetchmail in self.browse(cursor, uid, ids):
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
config_vals = {'port': 993,
@ -118,7 +128,8 @@ class FetchmailServer(osv.osv):
if serv_config.has_section(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):
config_vals.update(serv_config.items(custom_section_name))
@ -134,7 +145,8 @@ class FetchmailServer(osv.osv):
string='Server',
type="char",
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,
method=True,
string='Port',
@ -153,22 +165,28 @@ class FetchmailServer(osv.osv):
string='Is SSL',
type="boolean",
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,
method=True,
string='Keep Attachments',
type="boolean",
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,
method=True,
string='Keep Original',
type="boolean",
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,
method=True,
string='Username',
@ -181,4 +199,4 @@ class FetchmailServer(osv.osv):
type="char",
multi='income_mail_config',
size=64)}
FetchmailServer()
FetchmailServer()

8
mass_editing/__init__.py

@ -2,7 +2,8 @@
##############################################################################
#
# 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>)
#
# 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:

23
mass_editing/__openerp__.py

@ -2,7 +2,8 @@
##############################################################################
#
# 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>)
#
# 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/>
#
##############################################################################
{
"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.
"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.
The video explaining the feature is available at http://t.co/wukYMx1A
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'],
'init_xml': [],

95
mass_editing/mass_editing.py

@ -2,7 +2,8 @@
##############################################################################
#
# 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>)
#
# 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/>
#
##############################################################################
from osv import fields, osv
from tools.translate import _
class mass_object(osv.osv):
_name = "mass.object"
_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')
}
@ -48,7 +53,8 @@ class mass_object(osv.osv):
active_model_obj = self.pool.get(model_data.model)
if active_model_obj._inherits:
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:
model_ids.append(found_model_ids[0])
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):
vals = {}
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):
src_obj = data.model_id.model
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
def unlink_action(self, cr, uid, ids, context=None):
for template in self.browse(cr, uid, ids, context=context):
try:
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:
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:
raise osv.except_osv(_("Warning"), _("Deletion of the action record failed."))
raise osv.except_osv(
_("Warning"),
_("Deletion of the action record failed."))
return True
def unlink(self, cr, uid, ids, context=None):

7
mass_editing/wizard/__init__.py

@ -2,7 +2,8 @@
##############################################################################
#
# 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>)
#
# 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:

270
mass_editing/wizard/mass_editing_wizard.py

@ -2,7 +2,8 @@
##############################################################################
#
# 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>)
#
# This program is free software: you can redistribute it and/or modify
@ -21,74 +22,250 @@
##############################################################################
from osv import osv
from osv import fields
from lxml import etree
import tools
class mass_editing_wizard(osv.osv_memory):
_name = 'mass.editing.wizard'
_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'):
mass_object = self.pool.get('mass.object')
editing_data = mass_object.browse(cr, uid, context.get('mass_editing_object'), context)
mass_object = self.pool.get('mass.object')
editing_data = mass_object.browse(
cr, uid, context.get('mass_editing_object'), context)
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'})
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'})
model_obj = self.pool.get(context.get('active_model'))
for field in editing_data.field_ids:
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["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":
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:
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":
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':
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:
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':
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:
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()
result['arch'] = etree.tostring(root)
@ -117,12 +294,13 @@ class mass_editing_wizard(osv.osv_memory):
m2m_list.append((4, m2m_id))
model_vals[model_field] = m2m_list
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)
return result
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()

2
security_protector/__init__.py

@ -1 +1 @@
from . import security_protector
from . import security_protector

6
security_protector/__openerp__.py

@ -9,9 +9,11 @@
'description': """
Prevent security to be changed when module is updated
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
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',
'website': 'http://openerp.camptocamp.com',

41
security_protector/security_protector.py

@ -3,45 +3,50 @@
#
# Author Nicolas Bessi. Copyright Camptocamp SA
##############################################################################
from osv import fields, osv
from osv import osv
class IrModelAccess(osv.osv):
"We inherit ir model access to add specific write unlink and copy behavior"
_name = 'ir.model.access'
_inherit = "ir.model.access"
def _acces_can_be_modified(self, cr, uid, context=None):
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 context.get('manual_security_override', False):
return True
return False
else:
return True
def write(self, cr, uid, ids, vals, context=None):
res =True
res = True
context = context or {}
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
def unlink(self, cr, uid, ids, context=None):
res = True
context = context or {}
# I'm note sur about this one maybe we should do nothing
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)
else:
res = super(IrModelAccess, self).unlink(cr, uid, ids, 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)
else:
res = super(IrModelAccess, self).unlink(
cr, uid, ids, context=context)
return res
IrModelAccess()
IrModelAccess()

3
server_env_base_external_referentials/__init__.py

@ -18,5 +18,4 @@
# 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'],
"author": "Camptocamp",
'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 :

26
server_env_base_external_referentials/base_external_referentials.py

@ -24,31 +24,37 @@ from server_environment import serv_config
import logging
class external_referential(osv.osv):
_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 = {}
for referential in self.browse(cr, uid, ids, context):
values[referential.id] = {}
for field_name in field_names:
section_name = '.'.join((self._name.replace('.', '_'), referential.name))
section_name = '.'.join((self._name.replace('.', '_'),
referential.name))
try:
value = serv_config.get(section_name, field_name)
values[referential.id].update({field_name: value})
except:
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
_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()

14
server_environment/serv_config.py

@ -23,7 +23,7 @@ import os
import ConfigParser
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 .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'])
if not os.path.exists(ck_path) :
if not os.path.exists(ck_path):
raise Exception(
"Provided server environment does not exist, "
"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."""
if _bool is None:
_bool = _boolean_states
res = _bool[getattr(obj, attr).lower()]
setattr(obj, attr, res)
return res
@ -115,7 +118,7 @@ class ServerConfiguration(orm.TransientModel):
_conf_defaults = _Defaults()
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']
# Only show passwords in development
self.show_passwords = self.running_env in ('dev',)
@ -170,7 +173,7 @@ class ServerConfiguration(orm.TransientModel):
self._arch = etree.fromstring(arch)
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."""
res = super(ServerConfiguration, self).fields_view_get(cr, uid,
view_id,
@ -187,7 +190,6 @@ class ServerConfiguration(orm.TransientModel):
res['fields'] = xfields
return res
def default_get(self, cr, uid, fields_list, context=None):
res = {}
for key in self._conf_defaults:

2
server_environment_files/__init__.py

@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
##############################################################################

4
super_calendar/__init__.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# Copyright (C) 2012 Agile Business Group sagl (<http://www.agilebg.com>)
# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>)
#
@ -18,4 +18,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import super_calendar
from . import super_calendar

39
super_calendar/__openerp__.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# Copyright (C) 2012 Agile Business Group sagl (<http://www.agilebg.com>)
# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>)
#
@ -26,9 +26,13 @@
'description': """
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
=============
@ -37,39 +41,48 @@ After installing the module you can go to
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
: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
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:
.. 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
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
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',
'website': 'http://www.agilebg.com',
'license': 'AGPL-3',
'depends' : ['base'],
"data" : [
'depends': ['base'],
"data": [
'super_calendar_view.xml',
'cron_data.xml',
'security/ir.model.access.csv',
],
],
'demo': [],
'test': [],
'installable': True,

145
super_calendar/super_calendar.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# Copyright (C) 2012 Agile Business Group sagl (<http://www.agilebg.com>)
# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>)
#
@ -27,106 +27,151 @@ from datetime import datetime
from openerp import tools
from openerp.tools import safe_eval as eval
def _models_get(self, cr, uid, context=None):
obj = self.pool.get('ir.model')
ids = obj.search(cr, uid, [])
res = obj.read(cr, uid, ids, ['model', 'name'], context)
return [(r['model'], r['name']) for r in res]
class super_calendar_configurator(orm.Model):
_logger = logging.getLogger('super.calendar')
_name = 'super.calendar.configurator'
_columns = {
_columns = {
'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):
configurator_ids = self.search(cr, uid, [])
super_calendar_pool = self.pool.get('super.calendar')
# 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 line in configurator.line_ids:
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:
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 \
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
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
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:
duration = current_record[line.duration_field_id.name]
duration = current_record[
line.duration_field_id.name]
if line.description_type != 'code':
name = current_record[line.description_field_id.name]
name = current_record[
line.description_field_id.name]
else:
parse_dict = {'o': current_record}
mytemplate = Template(line.description_code)
name= mytemplate.render(**parse_dict)
name = mytemplate.render(**parse_dict)
super_calendar_values = {
'name': name,
'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,
'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,
'res_id': line.name.model+','+str(current_record['id']),
'res_id': line.name.model + ',' +
str(current_record['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')
return True
class super_calendar_configurator_line(orm.Model):
_name = 'super.calendar.configurator.line'
_columns = {
_columns = {
'name': fields.many2one('ir.model', 'Model', required=True),
'description': fields.char('Description', size=128, required=True),
'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"),
'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')]"),
'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),
'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)]"),
'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)]"),
'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)]"),
}
}
class super_calendar(orm.Model):
_name = 'super.calendar'
_columns = {
_columns = {
'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'),
'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'),
}
}

2
unserialize_field/__init__.py

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

41
unserialize_field/ir_model_fields.py

@ -42,20 +42,22 @@ class ir_model_fields(orm.Model):
context=context)
while True:
ids = pool_obj.search(
cr, uid,
cr, uid,
[(this.serialization_field_id.name, '!=', '{}')],
offset=offset*step, limit=step, context=context)
offset=offset * step, limit=step, context=context)
if not ids:
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)
offset += 1
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
finally:
cr.commit = commit_org
cr.commit = commit_org
return True
@ -82,7 +84,7 @@ class ir_model_fields(orm.Model):
raise orm.except_orm(
_("Error"),
_("One2many fields are not handled yet"))
# ORM prohibits to change the 'storing system' of the field
cr.execute("""
UPDATE ir_model_fields
@ -98,18 +100,17 @@ class ir_model_fields(orm.Model):
serialization_field_name, field_name,
context=None):
serialized_values = read_record[serialization_field_name]
if not field_name in serialized_values:
if field_name not in serialized_values:
return False
value = serialized_values.pop(field_name)
value = serialized_values.pop(field_name)
if pool_obj._columns[field_name]._type in ('many2many', 'one2many'):
value = [(6, 0, value)]
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/>.
#
##############################################################################
import model
from . import model

11
view_groups_id/__openerp__.py

@ -19,13 +19,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'group_ids for ir.ui.view',
'version': '1.0',
'description': """This addon is a backport of OpenERP 7.0's groups_id for
views.
The greatness lies in the fact that with that, you can have specific
inherited views for specific groups, so you can radically change a view
for some groups without having to redefine any of the window actions
@ -39,14 +38,10 @@
"category": "Dependency",
"depends": [
'base',
],
'css': [
],
],
'data': [
'view/ir_ui_view.xml',
],
'js': [
],
],
'installable': True,
'active': False,
'certificate': '',

2
view_groups_id/model/__init__.py

@ -19,4 +19,4 @@
# 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'
_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):
user_groups = frozenset(self.pool.get('res.users').browse(
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
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