From db704adb67985bd6eaad8c1dd3c5eac480aaa2ec Mon Sep 17 00:00:00 2001
From: "Mod2 Team (OpenERP)" <>
Date: Thu, 24 Jun 2010 11:33:46 +0530
Subject: [PATCH 01/38] [IMP] Audittrail: * Support action and workflow log
detail * Removed 'View log' menu * Modified form view of auditrail rule
object to display new fields TOFIX - Why need to restart server after
subcribe audittrail rule?
bzr revid: hmo@tinyerp.com-20100624060346-grlpb9h6blk4umvf
---
addons/audittrail/audittrail.py | 565 +++++++++++++++-----------
addons/audittrail/audittrail_view.xml | 34 +-
2 files changed, 353 insertions(+), 246 deletions(-)
diff --git a/addons/audittrail/audittrail.py b/addons/audittrail/audittrail.py
index 80166298edb..3a56f15ed3d 100644
--- a/addons/audittrail/audittrail.py
+++ b/addons/audittrail/audittrail.py
@@ -25,6 +25,7 @@ from tools.translate import _
import ir
import pooler
import time
+import tools
class audittrail_rule(osv.osv):
"""
@@ -33,25 +34,28 @@ class audittrail_rule(osv.osv):
_name = 'audittrail.rule'
_description = "Audittrail Rule"
_columns = {
- "name": fields.char("Rule Name", size=32, required=True),
- "object_id": fields.many2one('ir.model', 'Object', required=True),
- "user_id": fields.many2many('res.users', 'audittail_rules_users',
- 'user_id', 'rule_id', 'Users'),
- "log_read": fields.boolean("Log reads"),
- "log_write": fields.boolean("Log writes"),
- "log_unlink": fields.boolean("Log deletes"),
- "log_create": fields.boolean("Log creates"),
- "state": fields.selection((("draft", "Draft"),
- ("subscribed", "Subscribed")),
- "State", required=True),
- "action_id": fields.many2one('ir.actions.act_window', "Action ID"),
+ "name": fields.char("Rule Name", size=32, required=True),
+ "object_id": fields.many2one('ir.model', 'Object', required=True),
+ "user_id": fields.many2many('res.users', 'audittail_rules_users',
+ 'user_id', 'rule_id', 'Users', help="if User is not added then it will applicable for all users"),
+ "log_read": fields.boolean("Log Reads"),
+ "log_write": fields.boolean("Log Writes"),
+ "log_unlink": fields.boolean("Log Deletes"),
+ "log_create": fields.boolean("Log Creates"),
+ "log_action": fields.boolean("Log Action"),
+ "log_workflow": fields.boolean("Log Workflow"),
+ "state": fields.selection((("draft", "Draft"),
+ ("subscribed", "Subscribed")),
+ "State", required=True),
+ "action_id": fields.many2one('ir.actions.act_window', "Action ID"),
+
}
_defaults = {
- 'state': lambda *a: 'draft',
- 'log_create': lambda *a: 1,
- 'log_unlink': lambda *a: 1,
- 'log_write': lambda *a: 1,
+ 'state': lambda *a: 'draft',
+ 'log_create': lambda *a: 1,
+ 'log_unlink': lambda *a: 1,
+ 'log_write': lambda *a: 1,
}
_sql_constraints = [
@@ -65,7 +69,7 @@ class audittrail_rule(osv.osv):
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param ids: List of Auddittrail Rule’s IDs.
- @return: True
+ @return: True
"""
obj_action = self.pool.get('ir.actions.act_window')
obj_model = self.pool.get('ir.model.data')
@@ -74,20 +78,20 @@ class audittrail_rule(osv.osv):
obj = self.pool.get(thisrule.object_id.model)
if not obj:
raise osv.except_osv(
- _('WARNING: audittrail is not part of the pool'),
+ _('WARNING: audittrail is not part of the pool'),
_('Change audittrail depends -- Setting rule as DRAFT'))
self.write(cr, uid, [thisrule.id], {"state": "draft"})
val = {
- "name": 'View Log',
- "res_model": 'audittrail.log',
- "src_model": thisrule.object_id.model,
+ "name": 'View Log',
+ "res_model": 'audittrail.log',
+ "src_model": thisrule.object_id.model,
"domain": "[('object_id','=', " + str(thisrule.object_id.id) + "), ('res_id', '=', active_id)]"
}
- id = obj_action.create(cr, uid, val)
- self.write(cr, uid, [thisrule.id], {"state": "subscribed", "action_id": id})
+ action_id = obj_action.create(cr, uid, val)
+ self.write(cr, uid, [thisrule.id], {"state": "subscribed", "action_id": action_id})
keyword = 'client_action_relate'
- value = 'ir.actions.act_window,' + str(id)
+ value = 'ir.actions.act_window,' + str(action_id)
res = obj_model.ir_set(cr, uid, 'action', keyword, 'View_log_' + thisrule.object_id.model, [thisrule.object_id.model], value, replace=True, isobject=True, xml_id=False)
#End Loop
return True
@@ -98,7 +102,7 @@ class audittrail_rule(osv.osv):
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param ids: List of Auddittrail Rule’s IDs.
- @return: True
+ @return: True
"""
obj_action = self.pool.get('ir.actions.act_window')
val_obj = self.pool.get('ir.values')
@@ -127,17 +131,28 @@ class audittrail_log(osv.osv):
_name = 'audittrail.log'
_description = "Audittrail Log"
+ def _name_get_resname(self, cr, uid, ids, *args):
+ data = {}
+ for resname in self.browse(cr, uid, ids,[]):
+ model_object = resname.object_id
+ res_id = resname.res_id
+ if model_object and res_id:
+ model_pool = self.pool.get(model_object.model)
+ res = model_pool.read(cr, uid, res_id, ['name'])
+ data[resname.id] = res['name']
+ else:
+ data[resname.id] = False
+ return data
+
_columns = {
- "name": fields.char("Name", size=32),
- "object_id": fields.many2one('ir.model', 'Object'),
- "user_id": fields.many2one('res.users', 'User'),
- "method": fields.selection((('read', 'Read'),
- ('write', 'Write'),
- ('unlink', 'Delete'),
- ('create', 'Create')), "Method"),
- "timestamp": fields.datetime("Date"),
- "res_id": fields.integer('Resource Id'),
- "line_ids": fields.one2many('audittrail.log.line', 'log_id', 'Log lines'),
+ "name": fields.char("Resource Name",size=64),
+ # "name": fields.function(_name_get_resname, type='char', string='Resource Name', method=True),
+ "object_id": fields.many2one('ir.model', 'Object'),
+ "user_id": fields.many2one('res.users', 'User'),
+ "method": fields.char("Method", size=64),
+ "timestamp": fields.datetime("Date"),
+ "res_id": fields.integer('Resource Id'),
+ "line_ids": fields.one2many('audittrail.log.line', 'log_id', 'Log lines'),
}
_defaults = {
@@ -155,14 +170,14 @@ class audittrail_log_line(osv.osv):
_name = 'audittrail.log.line'
_description = "Log Line"
_columns = {
- 'field_id': fields.many2one('ir.model.fields', 'Fields', required=True),
- 'log_id': fields.many2one('audittrail.log', 'Log'),
- 'log': fields.integer("Log ID"),
- 'old_value': fields.text("Old Value"),
- 'new_value': fields.text("New Value"),
- 'old_value_text': fields.text('Old value Text'),
- 'new_value_text': fields.text('New value Text'),
- 'field_description': fields.char('Field Description', size=64),
+ 'field_id': fields.many2one('ir.model.fields', 'Fields', required=True),
+ 'log_id': fields.many2one('audittrail.log', 'Log'),
+ 'log': fields.integer("Log ID"),
+ 'old_value': fields.text("Old Value"),
+ 'new_value': fields.text("New Value"),
+ 'old_value_text': fields.text('Old value Text'),
+ 'new_value_text': fields.text('New value Text'),
+ 'field_description': fields.char('Field Description', size=64),
}
audittrail_log_line()
@@ -170,276 +185,366 @@ audittrail_log_line()
class audittrail_objects_proxy(osv_pool):
""" Uses Object proxy for auditing changes on object of subscribed Rules"""
-
- def get_value_text(self, cr, uid, field_name, values, object, context=None):
+
+ def get_value_text(self, cr, uid, field_name, values, model, context=None):
"""
Gets textual values for the fields
e.g.: For field of type many2one it gives its name value instead of id
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
- @param field_name: List of fields for text values
- @param values: Values for field to be converted into textual values
- @return: values: List of textual values for given fields
+ @param field_name: List of fields for text values
+ @param values: Values for field to be converted into textual values
+ @return: values: List of textual values for given fields
"""
if not context:
context = {}
pool = pooler.get_pool(cr.dbname)
- f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', field_name), ('model_id', '=', object.id)])
- if f_id:
- field = pool.get('ir.model.fields').read(cr, uid, f_id)[0]
- model = field['relation']
+ field_pool = pool.get('ir.model.fields')
+ model_pool = pool.get('ir.model')
+ obj_pool = pool.get(model.model)
+ if obj_pool._inherits:
+ inherits_ids = model_pool.search(cr, uid, [('model', '=', obj_pool._inherits.keys()[0])])
+ field_ids = field_pool.search(cr, uid, [('name', '=', field_name), ('model_id', 'in', (model.id, inherits_ids[0]))])
+ else:
+ field_ids = field_pool.search(cr, uid, [('name', '=', field_name), ('model_id', '=', model.id)])
+ field_id = field_ids and field_ids[0] or False
+ assert field_id, _("'%s' field is not exits in '%s' model" %(field_name, model.model))
- if field['ttype'] == 'many2one':
- if values:
- if type(values) == tuple:
- values = values[0]
- val = pool.get(model).read(cr, uid, [values], [pool.get(model)._rec_name])
- if val:
- return val[0][pool.get(model)._rec_name]
- elif field['ttype'] == 'many2many':
- value = []
- if values:
- for id in values:
- val = pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
- if val:
- value.append(val[0][pool.get(model)._rec_name])
- return value
+ field = field_pool.read(cr, uid, field_id)
+ relation_model = field['relation']
+ relation_model_pool = relation_model and pool.get(relation_model) or False
- elif field['ttype'] == 'one2many':
- if values:
- value = []
- for id in values:
- val = pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
+ if field['ttype'] == 'many2one':
+ res = False
+ relation_id = False
+ if values and type(values) == tuple:
+ relation_id = values[0]
+ if relation_id and relation_model_pool:
+ relation_model_object = relation_model_pool.read(cr, uid, relation_id, [relation_model_pool._rec_name])
+ res = relation_model_object[relation_model_pool._rec_name]
+ return res
- if val:
- value.append(val[0][pool.get(model)._rec_name])
- return value
- return values
+ elif field['ttype'] in ('many2many','one2many'):
+ res = []
+ for relation_model_object in relation_model_pool.read(cr, uid, values, [relation_model_pool._rec_name]):
+ res.append(relation_model_object[relation_model_pool._rec_name])
+ return res
- def create_log_line(self, cr, uid, id, object, lines=[]):
+ return values
+
+ def create_log_line(self, cr, uid, log_id, model, lines=[]):
"""
Creates lines for changed fields with its old and new values
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
- @param object: Object who's values are being changed
+ @param model: Object who's values are being changed
@param lines: List of values for line is to be created
"""
-
pool = pooler.get_pool(cr.dbname)
- obj = pool.get(object.model)
+ obj_pool = pool.get(model.model)
+ model_pool = pool.get('ir.model')
+ field_pool = pool.get('ir.model.fields')
+ log_line_pool = pool.get('audittrail.log.line')
#start Loop
for line in lines:
- if obj._inherits:
- inherits_ids = pool.get('ir.model').search(cr, uid, [('model', '=', obj._inherits.keys()[0])])
- f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (object.id, inherits_ids[0]))])
+ if obj_pool._inherits:
+ inherits_ids = model_pool.search(cr, uid, [('model', '=', obj_pool._inherits.keys()[0])])
+ field_ids = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (model.id, inherits_ids[0]))])
else:
- f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', '=', object.id)])
- if f_id:
- fields = pool.get('ir.model.fields').read(cr, uid, f_id)
- old_value = 'old_value' in line and line['old_value'] or ''
- new_value = 'new_value' in line and line['new_value'] or ''
- old_value_text = 'old_value_text' in line and line['old_value_text'] or ''
- new_value_text = 'new_value_text' in line and line['new_value_text'] or ''
-
- if old_value_text == new_value_text:
- continue
- if fields[0]['ttype'] == 'many2one':
- if type(old_value) == tuple:
- old_value = old_value[0]
- if type(new_value) == tuple:
- new_value = new_value[0]
- vals = {
- "log_id": id,
- "field_id": f_id[0],
- "old_value": old_value,
- "new_value": new_value,
- "old_value_text": old_value_text,
- "new_value_text": new_value_text,
- "field_description": fields[0]['field_description']
- }
- line_id = pool.get('audittrail.log.line').create(cr, uid, vals)
- cr.commit()
- #End Loop
+ field_ids = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', '=', model.id)])
+ field_id = field_ids and field_ids[0] or False
+ assert field_id, _("'%s' field is not exits in '%s' model" %(line['name'], model.model))
+
+ field = field_pool.read(cr, uid, field_id)
+ old_value = 'old_value' in line and line['old_value'] or ''
+ new_value = 'new_value' in line and line['new_value'] or ''
+ old_value_text = 'old_value_text' in line and line['old_value_text'] or ''
+ new_value_text = 'new_value_text' in line and line['new_value_text'] or ''
+
+ if old_value_text == new_value_text:
+ continue
+ if field['ttype'] == 'many2one':
+ if type(old_value) == tuple:
+ old_value = old_value[0]
+ if type(new_value) == tuple:
+ new_value = new_value[0]
+ vals = {
+ "log_id": log_id,
+ "field_id": field_id,
+ "old_value": old_value,
+ "new_value": new_value,
+ "old_value_text": old_value_text,
+ "new_value_text": new_value_text,
+ "field_description": field['field_description']
+ }
+ line_id = log_line_pool.create(cr, uid, vals)
+ #End Loop
return True
- def log_fct(self, db, uid, object, method, fct_src, *args):
+
+ def log_fct(self, db, uid, model, method, fct_src, *args):
"""
Logging function: This function is performs logging oprations according to method
@param db: the current database
@param uid: the current user’s ID for security checks,
@param object: Object who's values are being changed
- @param method: method to log: create, read, write, unlink
+ @param method: method to log: create, read, write, unlink
@param fct_src: execute method of Object proxy
-
- @return: Returns result as per method of Object proxy
+
+ @return: Returns result as per method of Object proxy
"""
- logged_uids = []
+ res2 = args
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
- obj_ids = pool.get('ir.model').search(cr, uid, [('model', '=', object)])
- model_object = pool.get('ir.model').browse(cr, uid, obj_ids)[0]
+ resource_pool = pool.get(model)
+ log_pool = pool.get('audittrail.log')
+ model_pool = pool.get('ir.model')
+
+ model_ids = model_pool.search(cr, uid, [('model', '=', model)])
+ model_id = model_ids and model_ids[0] or False
+ assert model_id, _("'%s' Model is not exits..." %(model))
+ model = model_pool.browse(cr, uid, model_id)
+
if method in ('create'):
- res_id = fct_src(db, uid, object, method, *args)
+ res_id = fct_src(db, uid, model.model, method, *args)
cr.commit()
- new_value = pool.get(model_object.model).read(cr, uid, [res_id], args[0].keys())[0]
- if 'id' in new_value:
- del new_value['id']
- if not logged_uids or uid in logged_uids:
- resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
- resource_name = resource_name and resource_name[0][1] or ''
- vals = {
- "method": method,
- "object_id": model_object.id,
- "user_id": uid, "res_id": res_id,
- "name": resource_name
- }
- id = pool.get('audittrail.log').create(cr, uid, vals)
- lines = []
- for field in new_value:
- if new_value[field]:
- line = {
- 'name': field,
- 'new_value': new_value[field],
- 'new_value_text': self.get_value_text(cr, uid, field, new_value[field], model_object)
- }
- lines.append(line)
- self.create_log_line(cr, uid, id, model_object, lines)
+ resource = resource_pool.read(cr, uid, res_id, args[0].keys())
+ vals = {
+ "method": method,
+ "object_id": model.id,
+ "user_id": uid,
+ "res_id": resource['id'],
+ }
+ if 'id' in resource:
+ del resource['id']
+ log_id = log_pool.create(cr, uid, vals)
+ lines = []
+ for field in resource:
+ line = {
+ 'name': field,
+ 'new_value': resource[field],
+ 'new_value_text': self.get_value_text(cr, uid, field, resource[field], model)
+ }
+ lines.append(line)
+ self.create_log_line(cr, uid, log_id, model, lines)
+
cr.commit()
cr.close()
return res_id
- if method in ('write'):
- res_ids = args[0]
- for res_id in res_ids:
- old_values = pool.get(model_object.model).read(cr, uid, res_id, args[1].keys())
- old_values_text = {}
- for field in args[1].keys():
- old_values_text[field] = self.get_value_text(cr, uid, field, old_values[field], model_object)
- res = fct_src(db, uid, object, method, *args)
- cr.commit()
- if res:
- new_values = pool.get(model_object.model).read(cr, uid, res_ids, args[1].keys())[0]
- if not logged_uids or uid in logged_uids:
- resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
- resource_name = resource_name and resource_name[0][1] or ''
- id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
- lines = []
- for field in args[1].keys():
- if args[1].keys():
- line = {
- 'name': field,
- 'new_value': field in new_values and new_values[field] or '',
- 'old_value': field in old_values and old_values[field] or '',
- 'new_value_text': self.get_value_text(cr, uid, field, new_values[field], model_object),
- 'old_value_text': old_values_text[field]
- }
- lines.append(line)
- cr.commit()
- self.create_log_line(cr, uid, id, model_object, lines)
- cr.close()
- return res
-
- if method in ('read'):
+ elif method in ('read'):
res_ids = args[0]
old_values = {}
- res = fct_src(db, uid, object, method, *args)
+ res = fct_src(db, uid, model.model, method, *args)
if type(res) == list:
-
for v in res:
old_values[v['id']] = v
else:
old_values[res['id']] = res
for res_id in old_values:
- if not logged_uids or uid in logged_uids:
- resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
- resource_name = resource_name and resource_name[0][1] or ''
- id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
- lines = []
- for field in old_values[res_id]:
- if old_values[res_id][field]:
- line = {
- 'name': field,
- 'old_value': old_values[res_id][field],
- 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
- }
- lines.append(line)
- cr.commit()
- self.create_log_line(cr, uid, id, model_object, lines)
+ vals = {
+ "method": method,
+ "object_id": model.id,
+ "user_id": uid,
+ "res_id": res_id,
+
+ }
+ log_id = log_pool.create(cr, uid, vals)
+ lines = []
+ for field in old_values[res_id]:
+ line = {
+ 'name': field,
+ 'old_value': old_values[res_id][field],
+ 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model)
+ }
+ lines.append(line)
+
+ self.create_log_line(cr, uid, log_id, model, lines)
+
cr.close()
return res
- if method in ('unlink'):
+ elif method in ('unlink'):
res_ids = args[0]
old_values = {}
for res_id in res_ids:
- old_values[res_id] = pool.get(model_object.model).read(cr, uid, res_id, [])
+ old_values[res_id] = resource_pool.read(cr, uid, res_id)
for res_id in res_ids:
- if not logged_uids or uid in logged_uids:
- resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
- resource_name = resource_name and resource_name[0][1] or ''
- id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
- lines = []
- for field in old_values[res_id]:
- if old_values[res_id][field]:
- line = {
- 'name': field,
- 'old_value': old_values[res_id][field],
- 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
- }
- lines.append(line)
- cr.commit()
- self.create_log_line(cr, uid, id, model_object, lines)
- res = fct_src(db, uid, object, method, *args)
+ vals = {
+ "method": method,
+ "object_id": model.id,
+ "user_id": uid,
+ "res_id": res_id,
+
+ }
+ log_id = log_pool.create(cr, uid, vals)
+ lines = []
+ for field in old_values[res_id]:
+ if field in ('id'):
+ continue
+ line = {
+ 'name': field,
+ 'old_value': old_values[res_id][field],
+ 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model)
+ }
+ lines.append(line)
+
+ self.create_log_line(cr, uid, log_id, model, lines)
+ res = fct_src(db, uid, model.model, method, *args)
+ cr.commit()
cr.close()
return res
- cr.close()
+ else:
+ res_ids = args[0]
+ old_values = {}
+ fields = []
+ if len(args)>1 and type(args[1]) == dict:
+ fields = args[1].keys()
+ if type(res_ids) in (long, int):
+ res_ids = [res_ids]
+ if res_ids:
+ for resource in resource_pool.read(cr, uid, res_ids):
+ resource_id = resource['id']
+ if 'id' in resource:
+ del resource['id']
+ old_values_text = {}
+ old_value = {}
+ for field in resource.keys():
+ old_value[field] = resource[field]
+ old_values_text[field] = self.get_value_text(cr, uid, field, resource[field], model)
+ old_values[resource_id] = {'text':old_values_text, 'value': old_value}
- def execute(self, db, uid, object, method, *args, **kw):
+ res = fct_src(db, uid, model.model, method, *args)
+ cr.commit()
+
+ if res_ids:
+ for resource in resource_pool.read(cr, uid, res_ids):
+ resource_id = resource['id']
+ if 'id' in resource:
+ del resource['id']
+ vals = {
+ "method": method,
+ "object_id": model.id,
+ "user_id": uid,
+ "res_id": resource_id,
+ }
+
+
+ log_id = log_pool.create(cr, uid, vals)
+ lines = []
+ for field in resource.keys():
+ line = {
+ 'name': field,
+ 'new_value': resource[field],
+ 'old_value': old_values[resource_id]['value'][field],
+ 'new_value_text': self.get_value_text(cr, uid, field, resource[field], model),
+ 'old_value_text': old_values[resource_id]['text'][field]
+ }
+ lines.append(line)
+
+ self.create_log_line(cr, uid, log_id, model, lines)
+ cr.commit()
+ cr.close()
+ return res
+ return True
+
+
+
+ def execute(self, db, uid, model, method, *args, **kw):
"""
Overrides Object Proxy execute method
@param db: the current database
@param uid: the current user’s ID for security checks,
@param object: Object who's values are being changed
- @param method: method to log: create, read, write, unlink
-
- @return: Returns result as per method of Object proxy
+ @param method: get any method and create log
+
+ @return: Returns result as per method of Object proxy
"""
pool = pooler.get_pool(db)
+ model_pool = pool.get('ir.model')
+ rule_pool = pool.get('audittrail.rule')
cr = pooler.get_db(db).cursor()
cr.autocommit(True)
logged_uids = []
fct_src = super(audittrail_objects_proxy, self).execute
- def my_fct(db, uid, object, method, *args):
- field = method
+ def my_fct(db, uid, model, method, *args):
rule = False
- obj_ids = pool.get('ir.model').search(cr, uid, [('model', '=', object)])
- for obj_name in pool.obj_list():
- if obj_name == 'audittrail.rule':
+ model_ids = model_pool.search(cr, uid, [('model', '=', model)])
+ model_id = model_ids and model_ids[0] or False
+
+ for model_name in pool.obj_list():
+ if model_name == 'audittrail.rule':
rule = True
if not rule:
- return fct_src(db, uid, object, method, *args)
- if not obj_ids:
- return fct_src(db, uid, object, method, *args)
- rule_ids = pool.get('audittrail.rule').search(cr, uid, [('object_id', '=', obj_ids[0]), ('state', '=', 'subscribed')])
- if not rule_ids:
- return fct_src(db, uid, object, method, *args)
+ return fct_src(db, uid, model, method, *args)
+ if not model_id:
+ return fct_src(db, uid, model, method, *args)
- for thisrule in pool.get('audittrail.rule').browse(cr, uid, rule_ids):
+ rule_ids = rule_pool.search(cr, uid, [('object_id', '=', model_id), ('state', '=', 'subscribed')])
+ if not rule_ids:
+ return fct_src(db, uid, model, method, *args)
+
+ for thisrule in rule_pool.browse(cr, uid, rule_ids):
for user in thisrule.user_id:
logged_uids.append(user.id)
if not logged_uids or uid in logged_uids:
- if field in ('read', 'write', 'create', 'unlink'):
- if getattr(thisrule, 'log_' + field):
- return self.log_fct(db, uid, object, method, fct_src, *args)
- return fct_src(db, uid, object, method, *args)
- res = my_fct(db, uid, object, method, *args)
+ if method in ('read', 'write', 'create', 'unlink'):
+ if getattr(thisrule, 'log_' + method):
+ return self.log_fct(db, uid, model, method, fct_src, *args)
+
+ elif method not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink', 'write', 'create'):
+ if thisrule.log_action:
+ return self.log_fct(db, uid, model, method, fct_src, *args)
+
+ return fct_src(db, uid, model, method, *args)
+ res = my_fct(db, uid, model, method, *args)
cr.close()
return res
+
+ def exec_workflow(self, db, uid, model, method, *args, **argv):
+ pool = pooler.get_pool(db)
+ cr = pooler.get_db(db).cursor()
+ cr.autocommit(True)
+ logged_uids = []
+ fct_src = super(audittrail_objects_proxy, self).exec_workflow
+ field = method
+ rule = False
+ model_pool = pool.get('ir.model')
+ rule_pool = pool.get('audittrail.rule')
+ model_ids = model_pool.search(cr, uid, [('model', '=', model)])
+ for obj_name in pool.obj_list():
+ if obj_name == 'audittrail.rule':
+ rule = True
+ if not rule:
+ cr.close()
+ return super(audittrail_objects_proxy, self).exec_workflow(db, uid, model, method, *args, **argv)
+ if not model_ids:
+ cr.close()
+ return super(audittrail_objects_proxy, self).exec_workflow(db, uid, model, method, *args, **argv)
+
+ rule_ids = rule_pool.search(cr, uid, [('object_id', 'in', model_ids), ('state', '=', 'subscribed')])
+ if not rule_ids:
+ cr.close()
+ return super(audittrail_objects_proxy, self).exec_workflow(db, uid, model, method, *args, **argv)
+
+ for thisrule in rule_pool.browse(cr, uid, rule_ids):
+ for user in thisrule.user_id:
+ logged_uids.append(user.id)
+ if not logged_uids or uid in logged_uids:
+ if thisrule.log_workflow:
+ cr.close()
+ return self.log_fct(db, uid, model, method, fct_src, *args)
+ cr.close()
+ return super(audittrail_objects_proxy, self).exec_workflow(db, uid, model, method, *args, **argv)
+
+ cr.close()
+ return True
+
audittrail_objects_proxy()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/audittrail/audittrail_view.xml b/addons/audittrail/audittrail_view.xml
index a79e6a0b6c0..17b2594d470 100644
--- a/addons/audittrail/audittrail_view.xml
+++ b/addons/audittrail/audittrail_view.xml
@@ -12,12 +12,15 @@
-
+
-
+ Validate Ledger Postingsir.actions.act_windowvalidate.account.move.linesformform
- {'record_id' : active_id}
+ {'record_id' : active_id}new
-
+
-
+ Validate Ledger Postings
From 02a17546b8ea3a9b3a8cbd89e1a0c954f4639845 Mon Sep 17 00:00:00 2001
From: "Mod2 Team (OpenERP)" <>
Date: Thu, 24 Jun 2010 15:18:27 +0530
Subject: [PATCH 11/38] [IMP] Document: * Add new configuration wizard to set
resource directiory for Sale order, Product or Project * Support pptx2txt,
docx2txt in content index. * Add new configuration wizard to browse FTP
Server in document_ftp module * Improved Configuration wizard to set url of
FTP Server
bzr revid: hmo@tinyerp.com-20100624094827-9yfmtiiegoiu0asa
---
addons/document/__init__.py | 1 +
addons/document/__openerp__.py | 1 +
addons/document/document_directory.py | 2 +-
addons/document/document_view.xml | 7 +-
addons/document/security/ir.model.access.csv | 2 +
addons/document/std_index.py | 98 ++++++++++----
addons/document/wizard/__init__.py | 21 +++
.../document/wizard/document_configuration.py | 120 ++++++++++++++++++
.../wizard/document_configuration_view.xml | 50 ++++++++
addons/document_ftp/__init__.py | 2 +-
addons/document_ftp/__openerp__.py | 3 +-
addons/document_ftp/doc_conf_wizard.py | 117 -----------------
.../document_ftp/security/ir.model.access.csv | 7 +-
addons/document_ftp/wizard/__init__.py | 25 ++++
addons/document_ftp/wizard/ftp_browse.py | 61 +++++++++
.../document_ftp/wizard/ftp_browse_view.xml | 40 ++++++
.../document_ftp/wizard/ftp_configuration.py | 55 ++++++++
.../ftp_configuration_view.xml} | 19 +--
18 files changed, 466 insertions(+), 165 deletions(-)
create mode 100644 addons/document/wizard/__init__.py
create mode 100644 addons/document/wizard/document_configuration.py
create mode 100644 addons/document/wizard/document_configuration_view.xml
delete mode 100644 addons/document_ftp/doc_conf_wizard.py
create mode 100644 addons/document_ftp/wizard/__init__.py
create mode 100644 addons/document_ftp/wizard/ftp_browse.py
create mode 100644 addons/document_ftp/wizard/ftp_browse_view.xml
create mode 100644 addons/document_ftp/wizard/ftp_configuration.py
rename addons/document_ftp/{document_ftp_view.xml => wizard/ftp_configuration_view.xml} (76%)
diff --git a/addons/document/__init__.py b/addons/document/__init__.py
index 694989bdd79..12306b0499b 100644
--- a/addons/document/__init__.py
+++ b/addons/document/__init__.py
@@ -27,5 +27,6 @@ import directory_content
import directory_report
import document
import report
+import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document/__openerp__.py b/addons/document/__openerp__.py
index e7d3f767010..5e1aa939b54 100644
--- a/addons/document/__openerp__.py
+++ b/addons/document/__openerp__.py
@@ -45,6 +45,7 @@
'update_xml': [
'document_view.xml',
'document_data.xml',
+ 'wizard/document_configuration_view.xml',
'security/document_security.xml',
'security/ir.model.access.csv',
'report/document_report_view.xml',
diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py
index 1d0bbf3ed7c..e275adcb5c6 100644
--- a/addons/document/document_directory.py
+++ b/addons/document/document_directory.py
@@ -112,7 +112,7 @@ class document_directory(osv.osv):
while d2 and d2.parent_id:
s = d2.name + (s and ('/' + s) or '')
d2 = d2.parent_id
- res.append((d.id, s))
+ res.append((d.id, s or d.name))
return res
def get_full_path(self, cr, uid, dir_id, context=None):
diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml
index 29f3617d64f..8fcd791144e 100644
--- a/addons/document/document_view.xml
+++ b/addons/document/document_view.xml
@@ -193,10 +193,7 @@
-
-
-
@@ -219,7 +216,9 @@
-
+
+
+
diff --git a/addons/document/security/ir.model.access.csv b/addons/document/security/ir.model.access.csv
index 245bcbffeab..a4412195745 100644
--- a/addons/document/security/ir.model.access.csv
+++ b/addons/document/security/ir.model.access.csv
@@ -19,3 +19,5 @@
"access_report_document_file_group_system","report.document.file group system","model_report_document_file","base.group_system",1,0,0,0
"access_report_document_wall_group_document_manager","report.document.wall document manager","model_report_document_wall","document.group_document_manager",1,0,0,0
"access_report_document_wall_group_system","report.document.wall group system","model_report_document_wall","base.group_system",1,0,0,0
+"access_document_configuation","document.configuration document manager","model_document_configuration","document.group_document_manager",1,1,1,1
+"access_document_configuation_system","document.configuration group system","model_document_configuration","base.group_system",1,1,1,1
diff --git a/addons/document/std_index.py b/addons/document/std_index.py
index c4f7d787d4d..2facb14eeb3 100644
--- a/addons/document/std_index.py
+++ b/addons/document/std_index.py
@@ -19,10 +19,11 @@
#
##############################################################################
+from content_index import indexer, cntIndex
+from subprocess import Popen, PIPE
import StringIO
import odt2txt
-from subprocess import Popen, PIPE
-from content_index import indexer, cntIndex
+import sys, zipfile, xml.dom.minidom
def _to_unicode(s):
try:
@@ -36,6 +37,15 @@ def _to_unicode(s):
except UnicodeError:
return s
+def textToString(element) :
+ buffer = u""
+ for node in element.childNodes :
+ if node.nodeType == xml.dom.Node.TEXT_NODE :
+ buffer += node.nodeValue
+ elif node.nodeType == xml.dom.Node.ELEMENT_NODE :
+ buffer += textToString(node)
+ return buffer
+
class TxtIndex(indexer):
def _getMimeTypes(self):
return ['text/plain','text/html','text/diff','text/xml', 'text/*',
@@ -44,7 +54,7 @@ class TxtIndex(indexer):
def _getExtensions(self):
return ['.txt', '.py']
- def _doIndexContent(self, content):
+ def _doIndexContent(self,content):
return content
cntIndex.register(TxtIndex())
@@ -56,15 +66,24 @@ class PptxIndex(indexer):
def _getExtensions(self):
return ['.pptx']
- def _doIndexFile(self, fname):
- # pptx2txt.pl package not support in windows platform.
- # Download pptx2txt package from http://sourceforge.net/projects/pptx2txt/" link.
- # To install this tool, just copy pptx2txt.pl to appropriate place (e.g. /usr/bin directory)
- fp = Popen(['pptx2txt.pl', fname], shell=False, stdout=PIPE).stdout
- fp.read()
- file_obj = open(str(fname + ".txt"), "r")
- data = file_obj.read()
- return _to_unicode(data)
+ def _doIndexFile(self,fname):
+ def toString () :
+ """ Converts the document to a string. """
+ buffer = u""
+ for val in ["a:t"]:
+ for paragraph in content.getElementsByTagName(val) :
+ buffer += textToString(paragraph) + "\n"
+ return buffer
+
+ data = []
+ zip = zipfile.ZipFile(fname)
+ files = filter(lambda x: x.startswith('ppt/slides/slide'), zip.namelist())
+ for i in range(1, len(files) + 1):
+ content = xml.dom.minidom.parseString(zip.read('ppt/slides/slide%s.xml' % str(i)))
+ res = toString().encode('ascii','replace')
+ data.append(res)
+
+ return _to_unicode('\n'.join(data))
cntIndex.register(PptxIndex())
@@ -76,33 +95,60 @@ class DocIndex(indexer):
return ['.doc']
def _doIndexFile(self,fname):
- fp = Popen(['antiword', fname], shell=False, stdout=PIPE).stdout
- return _to_unicode( fp.read())
+ #fp = Popen(['antiword', fname], shell=False, stdout=PIPE).stdout
+ return _to_unicode( 'None')
cntIndex.register(DocIndex())
class DocxIndex(indexer):
def _getMimeTypes(self):
return [ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
-
+
def _getExtensions(self):
return ['.docx']
- def _doIndexFile(self, fname):
- # docx2txt.pl package not support in windows platform.
- # Download docx2txt package from "http://sourceforge.net/projects/docx2txt/" link.
- # In case, you don't want to use Makefile for installation, you can follow these steps for manual installation.
- # Copy docx2txt.pl, docx2txt.sh and docx2txt.config to appropriate place (e.g. /usr/bin directory) . used following command.
- # --> cp docx2txt.pl docx2txt.sh docx2txt.config /usr/bin/
+ def _doIndexFile(self,fname):
+ zip = zipfile.ZipFile(fname)
+ content = xml.dom.minidom.parseString(zip.read("word/document.xml"))
+ def toString () :
+ """ Converts the document to a string. """
+ buffer = u""
+ for val in ["w:p", "w:h", "text:list"]:
+ for paragraph in content.getElementsByTagName(val) :
+ buffer += textToString(paragraph) + "\n"
+ return buffer
- fp = Popen(['docx2txt.pl', fname], shell=False, stdout=PIPE).stdout
- fp.read()
- file_obj = open(str(fname + ".txt"), "r")
- data = file_obj.read()
- return _to_unicode(data)
+ res = toString().encode('ascii','replace')
+
+ return _to_unicode(res)
cntIndex.register(DocxIndex())
+
+class XlsxIndex(indexer):
+ def _getMimeTypes(self):
+ return [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
+
+ def _getExtensions(self):
+ return ['.xlsx']
+
+ def _doIndexFile(self,fname):
+ zip = zipfile.ZipFile(fname)
+ content = xml.dom.minidom.parseString(zip.read("xl/sharedStrings.xml"))
+ def toString () :
+ """ Converts the document to a string. """
+ buffer = u""
+ for val in ["t"]:
+ for paragraph in content.getElementsByTagName(val) :
+ buffer += textToString(paragraph) + "\n"
+ return buffer
+
+ res = toString().encode('ascii','replace')
+
+ return _to_unicode(res)
+
+cntIndex.register(XlsxIndex())
+
class PdfIndex(indexer):
def _getMimeTypes(self):
return [ 'application/pdf']
diff --git a/addons/document/wizard/__init__.py b/addons/document/wizard/__init__.py
new file mode 100644
index 00000000000..b143b583426
--- /dev/null
+++ b/addons/document/wizard/__init__.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+import document_configuration
diff --git a/addons/document/wizard/document_configuration.py b/addons/document/wizard/document_configuration.py
new file mode 100644
index 00000000000..a497eb6af90
--- /dev/null
+++ b/addons/document/wizard/document_configuration.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+from osv import osv, fields
+class document_configuration(osv.osv_memory):
+
+ _name='document.configuration'
+ _description = 'Auto Directory Configuration'
+ _inherit = 'res.config'
+
+ _columns = {
+ 'sale_order' : fields.boolean('Sale Order', help="Auto directory configuration for Sale Orders and Quotation with report."),
+ 'product' : fields.boolean('Product', help="Auto directory configuration for Products."),
+ 'project': fields.boolean('Project', help="Auto directory configuration for Projects."),
+ }
+
+
+ def execute(self, cr, uid, ids, context=None):
+ conf_id = ids and ids[0] or False
+ conf = self.browse(cr, uid, conf_id, context)
+ dir_pool = self.pool.get('document.directory')
+ data_pool = self.pool.get('ir.model.data')
+ model_pool = self.pool.get('ir.model')
+ content_pool = self.pool.get('document.directory.content')
+ if conf.sale_order and self.pool.get('sale.order'):
+ # Sale order
+ dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_sale_order_all')
+ if dir_data_id:
+ sale_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
+ else:
+ sale_dir_id = data_pool.create(cr, uid, {'name': 'Sale Orders'})
+ mid = model_pool.search(cr, uid, [('model','=','sale.order')])
+ dir_pool.write(cr, uid, [sale_dir_id], {
+ 'type':'ressource',
+ 'ressource_type_id': mid[0],
+ 'domain': '[]',
+ })
+ # Qutation
+ dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_sale_order_quote')
+ if dir_data_id:
+ quta_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
+ else:
+ quta_dir_id = data_pool.create(cr, uid, {'name': 'Sale Quotations'})
+
+ dir_pool.write(cr, uid, [quta_dir_id], {
+ 'type':'ressource',
+ 'ressource_type_id': mid[0],
+ 'domain': "[('state','=','draft')]",
+ })
+ # Sale Order Report
+ order_report_data_id = data_pool._get_id(cr, uid, 'sale', 'report_sale_order')
+ if order_report_data_id:
+ order_report_id = data_pool.browse(cr, uid, order_report_data_id, context=context).res_id
+
+ content_pool.create(cr, uid, {
+ 'name': "Print Order",
+ 'suffix': "_print",
+ 'report_id': order_report_id,
+ 'extension': '.pdf',
+ 'include_name': 1,
+ 'directory_id': sale_dir_id,
+ })
+
+ content_pool.create(cr, uid, {
+ 'name': "Print Qutation",
+ 'suffix': "_print",
+ 'report_id': order_report_id,
+ 'extension': '.pdf',
+ 'include_name': 1,
+ 'directory_id': quta_dir_id,
+ })
+
+
+ if conf.product and self.pool.get('product.product'):
+ # Product
+ dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_product')
+ if dir_data_id:
+ product_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
+ else:
+ product_dir_id = data_pool.create(cr, uid, {'name': 'Products'})
+
+ mid = model_pool.search(cr, uid, [('model','=','product.product')])
+ dir_pool.write(cr, uid, [product_dir_id], {
+ 'type':'ressource',
+ 'ressource_type_id': mid[0],
+ })
+
+ if conf.project and self.pool.get('account.analytic.account'):
+ # Project
+ dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_project')
+ if dir_data_id:
+ project_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
+ else:
+ project_dir_id = data_pool.create(cr, uid, {'name': 'Projects'})
+
+ mid = model_pool.search(cr, uid, [('model','=','account.analytic.account')])
+ dir_pool.write(cr, uid, [project_dir_id], {
+ 'type':'ressource',
+ 'ressource_type_id': mid[0],
+ 'domain': '[]',
+ 'ressource_tree': 1
+ })
+document_configuration()
diff --git a/addons/document/wizard/document_configuration_view.xml b/addons/document/wizard/document_configuration_view.xml
new file mode 100644
index 00000000000..c6f40deb429
--- /dev/null
+++ b/addons/document/wizard/document_configuration_view.xml
@@ -0,0 +1,50 @@
+
+
+
+ Auto Configure Directory
+ document.configuration
+ form
+
+
+
+
+ Auto Configure
+
+
+ Resource Directory Configuration
+
+
+ Choose the following Resouces to auto directory configuration.
+
+
+ 12
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Auto Configure Directory
+ ir.actions.act_window
+ document.configuration
+
+ form
+ form
+ new
+
+
+
+
+
+
+
+
diff --git a/addons/document_ftp/__init__.py b/addons/document_ftp/__init__.py
index 45ec4a6b67f..af514df8985 100644
--- a/addons/document_ftp/__init__.py
+++ b/addons/document_ftp/__init__.py
@@ -18,7 +18,7 @@
# along with this program. If not, see .
#
##############################################################################
-import doc_conf_wizard
import ftpserver
+import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document_ftp/__openerp__.py b/addons/document_ftp/__openerp__.py
index 18042dabf93..a2b88d0a2a2 100644
--- a/addons/document_ftp/__openerp__.py
+++ b/addons/document_ftp/__openerp__.py
@@ -33,7 +33,8 @@
'depends': ['base', 'document'],
'init_xml': [],
'update_xml': [
- 'document_ftp_view.xml',
+ 'wizard/ftp_configuration_view.xml',
+ 'wizard/ftp_browse_view.xml',
'security/ir.model.access.csv'
],
'demo_xml': [],
diff --git a/addons/document_ftp/doc_conf_wizard.py b/addons/document_ftp/doc_conf_wizard.py
deleted file mode 100644
index 0f2f970572e..00000000000
--- a/addons/document_ftp/doc_conf_wizard.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see
-#
-##############################################################################
-
-import base64
-
-from osv import osv, fields
-from osv.orm import except_orm
-from tools import config
-import urlparse
-
-import os
-
-class document_configuration_wizard(osv.osv_memory):
-
- _name='document.configuration.wizard'
- _description = 'Auto Directory Configuration'
- _inherit = 'res.config'
- _rec_name = 'host'
- _columns = {
- 'host': fields.char('Address', size=64,
- help="Server address or IP and port to which users should connect to for DMS access",
- required=True),
- }
-
- _defaults = {
- 'host': config.get('ftp_server_host', 'localhost') + ':' + config.get('ftp_server_port', '8021'),
- }
-
- def execute(self, cr, uid, ids, context=None):
- conf = self.browse(cr, uid, ids[0], context)
- obj=self.pool.get('document.directory')
- objid=self.pool.get('ir.model.data')
-
- if self.pool.get('sale.order'):
- id = objid._get_id(cr, uid, 'document', 'dir_sale_order_all')
- id = objid.browse(cr, uid, id, context=context).res_id
- mid = self.pool.get('ir.model').search(cr, uid, [('model','=','sale.order')])
- obj.write(cr, uid, [id], {
- 'type':'ressource',
- 'ressource_type_id': mid[0],
- 'domain': '[]',
- })
- aid = objid._get_id(cr, uid, 'sale', 'report_sale_order')
- aid = objid.browse(cr, uid, aid, context=context).res_id
-
- self.pool.get('document.directory.content').create(cr, uid, {
- 'name': "Print Order",
- 'suffix': "_print",
- 'report_id': aid,
- 'extension': '.pdf',
- 'include_name': 1,
- 'directory_id': id,
- })
- id = objid._get_id(cr, uid, 'document', 'dir_sale_order_quote')
- id = objid.browse(cr, uid, id, context=context).res_id
- obj.write(cr, uid, [id], {
- 'type':'ressource',
- 'ressource_type_id': mid[0],
- 'domain': "[('state','=','draft')]",
- })
-
- if self.pool.get('product.product'):
- id = objid._get_id(cr, uid, 'document', 'dir_product')
- id = objid.browse(cr, uid, id, context=context).res_id
- mid = self.pool.get('ir.model').search(cr, uid, [('model','=','product.product')])
- obj.write(cr, uid, [id], {
- 'type':'ressource',
- 'ressource_type_id': mid[0],
- })
-
- if self.pool.get('stock.location'):
- aid = objid._get_id(cr, uid, 'stock', 'report_product_history')
- aid = objid.browse(cr, uid, aid, context=context).res_id
-
- self.pool.get('document.directory.content').create(cr, uid, {
- 'name': "Product Stock",
- 'suffix': "_stock_forecast",
- 'report_id': aid,
- 'extension': '.pdf',
- 'include_name': 1,
- 'directory_id': id,
- })
-
- if self.pool.get('account.analytic.account'):
- id = objid._get_id(cr, uid, 'document', 'dir_project')
- id = objid.browse(cr, uid, id, context=context).res_id
- mid = self.pool.get('ir.model').search(cr, uid, [('model','=','account.analytic.account')])
- obj.write(cr, uid, [id], {
- 'type':'ressource',
- 'ressource_type_id': mid[0],
- 'domain': '[]',
- 'ressource_tree': 1
- })
-
- # Update the action for FTP browse.
- aid = objid._get_id(cr, uid, 'document_ftp', 'action_document_browse')
- aid = objid.browse(cr, uid, aid, context=context).res_id
- self.pool.get('ir.actions.url').write(cr, uid, [aid], {'url': 'ftp://'+(conf.host or 'localhost:8021')+'/'})
-document_configuration_wizard()
diff --git a/addons/document_ftp/security/ir.model.access.csv b/addons/document_ftp/security/ir.model.access.csv
index c9c082f4397..b3ffdbd5350 100644
--- a/addons/document_ftp/security/ir.model.access.csv
+++ b/addons/document_ftp/security/ir.model.access.csv
@@ -1,3 +1,6 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-"access_document_configuation_wizard","document.configuration.wizard document manager","model_document_configuration_wizard","document.group_document_manager",1,1,1,1
-"access_document_configuation_wizard_sytem","document.configuration.wizard group system","model_document_configuration_wizard","base.group_system",1,1,1,1
+"access_document_ftp_browse","document.ftp.browse document manager","model_document_ftp_browse","document.group_document_manager",1,1,1,1
+"access_document_ftp_browse","document.ftp.browse group system","model_document_ftp_browse","base.group_system",1,1,1,1
+"access_document_ftp_configuation","document.ftp.configuration document manager","model_document_ftp_configuration","document.group_document_manager",1,1,1,1
+"access_document_ftp_configuation_system","document.ftp.configuration group system","model_document_ftp_configuration","base.group_system",1,1,1,1
+
diff --git a/addons/document_ftp/wizard/__init__.py b/addons/document_ftp/wizard/__init__.py
new file mode 100644
index 00000000000..40997cc6b90
--- /dev/null
+++ b/addons/document_ftp/wizard/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+import ftp_browse
+import ftp_configuration
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document_ftp/wizard/ftp_browse.py b/addons/document_ftp/wizard/ftp_browse.py
new file mode 100644
index 00000000000..d033302f22a
--- /dev/null
+++ b/addons/document_ftp/wizard/ftp_browse.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+from osv import osv, fields
+from tools.translate import _
+from document_ftp import ftpserver
+class document_ftp_browse(osv.osv_memory):
+ _name = 'document.ftp.browse'
+ _description = 'Document FTP Browse'
+
+ _columns = {
+ 'url' : fields.char('FTP Server', size=64, required=True),
+ }
+
+ def default_get(self, cr, uid, fields, context=None):
+ res = {}
+ if 'url' in fields:
+ user_pool = self.pool.get('res.users')
+ current_user = user_pool.browse(cr, uid, uid, context=context)
+ dir_pool = self.pool.get('document.directory')
+ data_pool = self.pool.get('ir.model.data')
+ aid = data_pool._get_id(cr, uid, 'document_ftp', 'action_document_browse')
+ aid = data_pool.browse(cr, uid, aid, context=context).res_id
+ ftp_url = self.pool.get('ir.actions.url').browse(cr, uid, aid, context=context)
+ url = ftp_url.url and ftp_url.url.split('ftp://') or []
+ if url:
+ url = url[1]
+ else:
+ url = '%s:%s' %(ftpserver.HOST, ftpserver.PORT)
+ res['url'] = 'ftp://%s@%s'%(current_user.login, url)
+ return res
+
+ def browse_ftp(self, cr, uid, ids, context):
+ data_id = ids and ids[0] or False
+ data = self.browse(cr, uid, data_id, context)
+ final_url = data.url
+ return {
+ 'type': 'ir.actions.act_url',
+ 'url':final_url,
+ 'target': 'new'
+ }
+document_ftp_browse()
+
diff --git a/addons/document_ftp/wizard/ftp_browse_view.xml b/addons/document_ftp/wizard/ftp_browse_view.xml
new file mode 100644
index 00000000000..44cedccfc3e
--- /dev/null
+++ b/addons/document_ftp/wizard/ftp_browse_view.xml
@@ -0,0 +1,40 @@
+
+
+
+
+ Document FTP Browse
+ document.ftp.browse
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Document Browse
+ ir.actions.act_window
+ document.ftp.browse
+
+ form
+ form
+ new
+
+
+
+
+
+
diff --git a/addons/document_ftp/wizard/ftp_configuration.py b/addons/document_ftp/wizard/ftp_configuration.py
new file mode 100644
index 00000000000..40926bdf7c9
--- /dev/null
+++ b/addons/document_ftp/wizard/ftp_configuration.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
+import base64
+
+from osv import osv, fields
+from osv.orm import except_orm
+from tools import config
+import urlparse
+
+import os
+
+class document_ftp_configuration(osv.osv_memory):
+
+ _name='document.ftp.configuration'
+ _description = 'Auto Directory Configuration'
+ _inherit = 'res.config'
+ _rec_name = 'host'
+ _columns = {
+ 'host': fields.char('Address', size=64,
+ help="Server address or IP and port to which users should connect to for DMS access",
+ required=True),
+ }
+
+ _defaults = {
+ 'host': config.get('ftp_server_host', 'localhost') + ':' + config.get('ftp_server_port', '8021'),
+ }
+
+ def execute(self, cr, uid, ids, context=None):
+ conf = self.browse(cr, uid, ids[0], context)
+ dir_pool = self.pool.get('document.directory')
+ data_pool = self.pool.get('ir.model.data')
+ # Update the action for FTP browse.
+ aid = data_pool._get_id(cr, uid, 'document_ftp', 'action_document_browse')
+ aid = data_pool.browse(cr, uid, aid, context=context).res_id
+ self.pool.get('ir.actions.url').write(cr, uid, [aid], {'url': 'ftp://'+(conf.host or 'localhost:8021')+'/'})
+document_ftp_configuration()
diff --git a/addons/document_ftp/document_ftp_view.xml b/addons/document_ftp/wizard/ftp_configuration_view.xml
similarity index 76%
rename from addons/document_ftp/document_ftp_view.xml
rename to addons/document_ftp/wizard/ftp_configuration_view.xml
index d3f4c09498c..df3aee360af 100644
--- a/addons/document_ftp/document_ftp_view.xml
+++ b/addons/document_ftp/wizard/ftp_configuration_view.xml
@@ -3,26 +3,19 @@
Browse Filesftp://localhost:8021/
-
-
+
- Auto Configure Directory
- document.configuration.wizard
+ FTP Server Configuration
+ document.ftp.configurationform
- Auto Configure
+ FTP Server Configuration
FTP Server Configuration
@@ -43,9 +36,9 @@
- Auto Configure Directory
+ FTP Server Configurationir.actions.act_window
- document.configuration.wizard
+ document.ftp.configurationformform
From c0016dada450869715bc56e60ff2bb6ddcd6a70e Mon Sep 17 00:00:00 2001
From: Mantavya Gajjar
Date: Thu, 24 Jun 2010 15:59:09 +0530
Subject: [PATCH 12/38] [IMP]: act window on account.move to zoom out
accounting lines
bzr revid: mga@tinyerp.com-20100624102909-3w563h8o98x6us07
---
addons/account/account_view.xml | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml
index 22d47ca540e..aa3bf57da31 100644
--- a/addons/account/account_view.xml
+++ b/addons/account/account_view.xml
@@ -1053,7 +1053,15 @@
-
+
+
+
Journal Entries to Reviewir.actions.act_window
@@ -1230,17 +1238,17 @@
id="menu_action_account_bank_reconcile_check_tree"
parent="periodical_processing_reconciliation" groups="group_account_user" />
-
-
+
Date: Thu, 24 Jun 2010 16:12:25 +0530
Subject: [PATCH 13/38] [FIX] l10n_be: vat declaration: the tax code 91 can
only be send for the declaration of December
bzr revid: ach@tinyerp.com-20100624104225-14w73gpdj3j8quoe
---
addons/l10n_be/wizard/l10n_be_account_vat_declaration.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py
index a5e664c833a..155e40b256b 100644
--- a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py
+++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py
@@ -106,6 +106,9 @@ class l10n_be_vat_declaration(osv.osv_memory):
data_of_file +='\n\t\t\n\t\t\t'
for item in tax_info:
+ if item['code'] == '91' and ending_month != 12:
+ #the tax code 91 can only be send for the declaration of December
+ continue
if item['code']:
if item['code'] == '71-72':
item['code']='71'
From e2a068e03f9a05dbd21cf3602acdadf074775139 Mon Sep 17 00:00:00 2001
From: Dhara
Date: Thu, 24 Jun 2010 16:22:26 +0530
Subject: [PATCH 14/38] [FIX] email_tempalte : view and error on email
bzr revid: dhara@dhara-laptop-20100624105226-4n11kv5289wnt96p
---
addons/email_template/email_template.py | 12 +++--
.../email_template/email_template_account.py | 8 ++--
.../email_template/email_template_mailbox.py | 7 +--
addons/email_template/email_template_view.xml | 44 +++++++++----------
4 files changed, 36 insertions(+), 35 deletions(-)
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 37b38774a7d..9190bd17e2b 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -108,7 +108,8 @@ class email_template(osv.osv):
'enforce_from_account':fields.many2one(
'email_template.account',
string="Enforce From Account",
- help="Emails will be sent only from this account."),
+ help="Emails will be sent only from this account(which are approved).",
+ domain=[('state','=','approved')]),
'from_email' : fields.related('enforce_from_account', 'email_id',
type='char', string='From',),
'def_to':fields.char(
@@ -580,8 +581,8 @@ class email_template(osv.osv):
context)
print 'Sending', mailbox_id
- self.pool.get('email_template.mailbox').send_this_mail(cursor, user, [mailbox_id], context)
return mailbox_id
+
def generate_mail(self,
cursor,
@@ -595,6 +596,7 @@ class email_template(osv.osv):
if not template:
raise Exception("The requested template could not be loaded")
print 'loaded', record_ids
+ result = True
for record_id in record_ids:
mailbox_id = self._generate_mailbox_item_from_template(
cursor,
@@ -625,7 +627,9 @@ class email_template(osv.osv):
{'folder':'outbox'},
context=context
)
- return True
+ # TODO : manage return value of all the records
+ result = self.pool.get('email_template.mailbox').send_this_mail(cursor, user, [mailbox_id], context)
+ return result
email_template()
@@ -641,7 +645,7 @@ class email_template_preview(osv.osv_memory):
if 'template_id' in context.keys():
ref_obj_id = self.pool.get('email.template').read(cr, uid, context['template_id'], ['object_name'], context)
ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id['object_name'][0], ['model'], context)['model']
- ref_obj_ids = self.pool.get(ref_obj_name).search(cr, uid, [], context=context)
+ ref_obj_ids = self.pool.get(ref_obj_name).search(cr, uid, [], 0, 20, 'id desc', context=context)
ref_obj_recs = self.pool.get(ref_obj_name).name_get(cr, uid, ref_obj_ids, context)
return ref_obj_recs
diff --git a/addons/email_template/email_template_account.py b/addons/email_template/email_template_account.py
index 0bcf3c3d099..52cff384ec4 100644
--- a/addons/email_template/email_template_account.py
+++ b/addons/email_template/email_template_account.py
@@ -266,7 +266,7 @@ class email_template_account(osv.osv):
return result
def send_mail(self, cr, uid, ids, addresses, subject='', body=None, payload=None, context=None):
- #TODO: Replace all this crap with a single email object
+ #TODO: Replace all this with a single email object
if body is None:
body = {}
if payload is None:
@@ -320,19 +320,20 @@ class email_template_account(osv.osv):
msg.attach(part)
except Exception, error:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:MIME Error\nDescription: %s") % (id, error))
- return error
+ return {'error_msg': "Server Send Error\nDescription: %s"%error}
try:
#print msg['From'],toadds
serv.sendmail(msg['From'], addresses_l['all'], msg.as_string())
except Exception, error:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Server Send Error\nDescription: %s") % (id, error))
- return error
+ return {'error_msg': "Server Send Error\nDescription: %s"%error}
#The mail sending is complete
serv.close()
logger.notifyChannel(_("Email Template"), netsvc.LOG_INFO, _("Mail from Account %s successfully Sent.") % (id))
return True
else:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id)
+ return {'error_msg':"Mail from Account %s failed. Probable Reason:Account not approved"% id}
def extracttime(self, time_as_string):
"""
@@ -394,7 +395,6 @@ class email_template_account(osv.osv):
return date_as_date
def send_receive(self, cr, uid, ids, context=None):
- self.get_mails(cr, uid, ids, context)
for id in ids:
ctx = context.copy()
ctx['filters'] = [('account_id', '=', id)]
diff --git a/addons/email_template/email_template_mailbox.py b/addons/email_template/email_template_mailbox.py
index fe9ca334d21..b89a9aeda26 100644
--- a/addons/email_template/email_template_mailbox.py
+++ b/addons/email_template/email_template_mailbox.py
@@ -56,18 +56,19 @@ class email_template_mailbox(osv.osv):
values['subject'] or u'',
{'text':values.get('body_text', u'') or u'', 'html':values.get('body_html', u'') or u''},
payload=payload, context=context)
-
if result == True:
self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
self.historise(cr, uid, [id], "Email sent successfully", context)
else:
- self.historise(cr, uid, [id], result, context)
+ error = result['error_msg']
+ self.historise(cr, uid, [id], error, context)
+
except Exception, error:
logger = netsvc.Logger()
logger.notifyChannel(_("Power Email"), netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error))
self.historise(cr, uid, [id], error, context)
self.write(cr, uid, id, {'state':'na'}, context)
- return True
+ return result
def historise(self, cr, uid, ids, message='', context=None):
for id in ids:
diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml
index 934a7324b07..12a24ca0934 100644
--- a/addons/email_template/email_template_view.xml
+++ b/addons/email_template/email_template_view.xml
@@ -8,33 +8,29 @@
form
@@ -53,7 +61,7 @@
search
-
+
@@ -144,6 +152,16 @@
+
+
+ Mailgateway Messages
+ mailgate.message
+ tree,form
+ form
+ [('history', '=', True)]
+
+
+
Mailgateway Threadsmailgate.thread
@@ -151,7 +169,7 @@
form
-
+
tree
@@ -166,7 +184,7 @@
-
Date: Thu, 24 Jun 2010 18:29:07 +0530
Subject: [PATCH 23/38] [IMP]: crm: While changing stage, If the stage has
on_change=True and Probability set the case probability should be changed
accordingly + Improved label
bzr revid: rpa@tinyerp.com-20100624125907-givvt2bn6dotmrgs
---
addons/crm/crm.py | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 7510bc48914..feb49ba7ae7 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -121,11 +121,16 @@ class crm_case(object):
s = self.get_stage_dict(cr, uid, ids, context=context)
section = self._name
+ stage_pool = self.pool.get('crm.case.stage')
for case in self.browse(cr, uid, ids, context):
if section in s:
st = case.stage_id.id or False
if st in s[section]:
- self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
+ data = {'stage_id': s[section][st]}
+ stage = stage_pool.browse(cr, uid, s[section][st], context=context)
+ if stage.on_change:
+ data.update({'probability': stage.probability})
+ self.write(cr, uid, [case.id], data)
return True
def get_stage_dict(self, cr, uid, ids, context=None):
@@ -162,12 +167,17 @@ class crm_case(object):
s = self.get_stage_dict(cr, uid, ids, context=context)
section = self._name
+ stage_pool = self.pool.get('crm.case.stage')
for case in self.browse(cr, uid, ids, context):
if section in s:
st = case.stage_id.id or False
s[section] = dict([(v, k) for (k, v) in s[section].iteritems()])
if st in s[section]:
- self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
+ data = {'stage_id': s[section][st]}
+ stage = stage_pool.browse(cr, uid, s[section][st], context=context)
+ if stage.on_change:
+ data.update({'probability': stage.probability})
+ self.write(cr, uid, [case.id], data)
return True
def onchange_partner_id(self, cr, uid, ids, part, email=False):
@@ -181,7 +191,8 @@ class crm_case(object):
"""
if not part:
return {'value': {'partner_address_id': False,
- 'email_from': False,
+ 'email_from': False,
+ 'phone': False
}}
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data = {'partner_address_id': addr['contact']}
@@ -200,7 +211,7 @@ class crm_case(object):
if not add:
return {'value': {'email_from': False}}
address = self.pool.get('res.partner.address').browse(cr, uid, add)
- return {'value': {'email_from': address.email}}
+ return {'value': {'email_from': address.email, 'phone': address.phone}}
def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context={}):
mailgate_pool = self.pool.get('mailgate.thread')
@@ -541,7 +552,7 @@ class crm_case_resource_type(osv.osv):
_rec_name = "name"
_columns = {
- 'name': fields.char('Case Resource Type', size=64, required=True, translate=True),
+ 'name': fields.char('Resource Type', size=64, required=True, translate=True),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'object_id': fields.many2one('ir.model', 'Object Name'),
}
From ddedf9148e3cebcabebfebe18d1cea4e833f5968 Mon Sep 17 00:00:00 2001
From: "rpa (Open ERP)"
Date: Thu, 24 Jun 2010 18:33:07 +0530
Subject: [PATCH 24/38] [IMP]: crm: History must create only if mail is sent
successfully [IMP]: crm: Subject should be prefixed by "Re: " when reply to
last mail [FIX]: crm: email is sent to email ids of email_cc field but it
was going as TO instead of CC
bzr revid: rpa@tinyerp.com-20100624130307-mt11m89tuvopiz7i
---
addons/crm/wizard/crm_forward_to_partner.py | 8 +++-
addons/crm/wizard/crm_send_email.py | 44 ++++++++++++---------
2 files changed, 31 insertions(+), 21 deletions(-)
diff --git a/addons/crm/wizard/crm_forward_to_partner.py b/addons/crm/wizard/crm_forward_to_partner.py
index 47bc02e0ca1..afae01ca2fe 100644
--- a/addons/crm/wizard/crm_forward_to_partner.py
+++ b/addons/crm/wizard/crm_forward_to_partner.py
@@ -183,14 +183,18 @@ class crm_lead_forward_to_partner(osv.osv_memory):
emails = [this.email_to]
body = case_pool.format_body(this.message)
email_from = this.email_from or False
- case_pool._history(cr, uid, [case], _('Forward'), history=True, email=this.email_to, details=body, email_from=email_from)
+ flag = False
flag = tools.email_send(
email_from,
emails,
this.subject,
body,
)
+ if flag:
+ case_pool._history(cr, uid, [case], _('Forward'), history=True, email=this.email_to, subject=this.subject, details=body, email_from=email_from)
+ if not flag:
+ raise osv.except_osv(_('Error!'), _('Unable to send mail. Please check SMTP is configured properly.'))
if this.add_cc:
case_pool.write(cr, uid, case.id, {'email_cc' : case.email_cc and case.email_cc + ', ' + this.email_to or this.email_to})
return {}
@@ -199,7 +203,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
message = []
lead_proxy = self.pool.get('crm.lead')
lead = lead_proxy.browse(cr, uid, lead_id, context=context)
- if lead.type == 'lead':
+ if not lead.type or lead.type == 'lead':
field_names = [
'partner_name', 'title', 'function', 'street', 'street2',
'zip', 'city', 'country_id', 'state_id', 'email_from',
diff --git a/addons/crm/wizard/crm_send_email.py b/addons/crm/wizard/crm_send_email.py
index 68930e2b85f..480ea0f10a1 100644
--- a/addons/crm/wizard/crm_send_email.py
+++ b/addons/crm/wizard/crm_send_email.py
@@ -23,6 +23,7 @@
from osv import osv, fields
from tools.translate import _
import base64
+import time
import tools
from crm import crm
@@ -96,42 +97,46 @@ class crm_send_new_email(osv.osv_memory):
else:
hist = hist_obj.browse(cr, uid, res_id)
message_id = hist.message_id
- model = hist.res_model
+ model = hist.model
model_pool = self.pool.get(model)
- case = model_pool.browse(cr, uid, hist.res_id)
- res_id = hist.res_id
- emails = [obj.email_to] + (obj.email_cc or '').split(',')
+ res_ids = model_pool.search(cr, uid, [('thread_id','=', hist.thread_id.id)])
+ res_id = res_ids and res_ids[0] or False
+ case = model_pool.browse(cr, uid, res_id)
+ emails = [obj.email_to]
+ email_cc = (obj.email_cc or '').split(',')
emails = filter(None, emails)
body = obj.text
body = case_pool.format_body(body)
email_from = getattr(obj, 'email_from', False)
- case_pool._history(cr, uid, [case], _('Send'), history=True, \
- email=obj.email_to, details=body, \
- subject=obj.subject, email_from=email_from, \
- message_id=message_id, attach=attach)
-
- x_headers = dict()
- #x_headers = {
- # 'Reply-To':"%s" % case.section_id.reply_to,
- #}
+ x_headers = {
+ 'model': model,
+ 'resource-id': res_id
+ }
if message_id:
x_headers['References'] = "%s" % (message_id)
- print case.section_id
+ flag = False
+
flag = tools.email_send(
email_from,
emails,
obj.subject,
body,
+ email_cc=email_cc,
attach=attach,
reply_to=case.section_id and case.section_id.reply_to or email_from,
openobject_id=str(case.id),
x_headers=x_headers
)
-
+ if not flag:
+ raise osv.except_osv(_('Error!'), _('Unable to send mail. Please check SMTP is configured properly.'))
if flag:
+ case_pool._history(cr, uid, [case], _('Send'), history=True, \
+ email=obj.email_to, details=body, \
+ subject=obj.subject, email_from=email_from, \
+ message_id=message_id, attach=attach)
if obj.state == 'unchanged':
pass
elif obj.state == 'done':
@@ -194,14 +199,15 @@ class crm_send_new_email(osv.osv_memory):
include_original = context and context.get('include_original', False) or False
res = {}
for hist in hist_obj.browse(cr, uid, res_ids, context=context):
- model = hist.res_model
+ model = hist.model
# In the case where the crm.case does not exist in the database
if not model:
return {}
model_pool = self.pool.get(model)
- res_id = hist.res_id or False
+ res_ids = model_pool.search(cr, uid, [('thread_id','=', hist.thread_id.id)])
+ res_id = res_ids and res_ids[0] or False
case = model_pool.browse(cr, uid, res_id)
if 'email_to' in fields:
res.update({'email_to': case.email_from or hist.email_from or False})
@@ -222,10 +228,10 @@ class crm_send_new_email(osv.osv_memory):
original = [header, sender, to, sentdate, desc, signature]
- res['text']= '\n'.join(original)
+ res['text']= '\n\n\n' + '\n'.join(original)
if 'subject' in fields:
- res.update({'subject': '[%s] %s' %(str(case.id), case.name or '')})
+ res.update({'subject': 'Re: %s' %(hist.name or '')})
if 'state' in fields:
res['state']='pending'
return res
From 903e0c69d14e88a1d239ac03e462cb1a4059ec00 Mon Sep 17 00:00:00 2001
From: "rpa (Open ERP)"
Date: Thu, 24 Jun 2010 18:44:01 +0530
Subject: [PATCH 25/38] [IMP]: crm: Improvements in crm as per testing on
http://piratepad.net/openerp-mod2-crm-bugs
bzr revid: rpa@tinyerp.com-20100624131401-gju5jnavucp6sc66
---
addons/crm/crm_lead.py | 4 +--
addons/crm/crm_lead_view.xml | 7 ++--
addons/crm/crm_meeting_view.xml | 36 +++++++++++++++++--
addons/crm/crm_opportunity.py | 3 +-
addons/crm/crm_opportunity_menu.xml | 6 ++--
addons/crm/crm_opportunity_view.xml | 14 +++++---
addons/crm/crm_phonecall.py | 4 +--
addons/crm/report/crm_lead_report.py | 2 +-
.../crm_opportunity_to_phonecall_view.xml | 4 ++-
9 files changed, 59 insertions(+), 21 deletions(-)
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index 2260a9f0965..dfa2a9b7009 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -139,8 +139,8 @@ and users by email"),
\nIf the case is in progress the state is set to \'Open\'.\
\nWhen the case is over, the state is set to \'Done\'.\
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
- 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('res_model','=',_name)]),
- 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('res_model','=',_name)]),
+ 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('model','=',_name)]),
+ 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
}
_defaults = {
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index c33339047d8..9fe9b8e6170 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -256,12 +256,11 @@
calendar
-
+
-
+
diff --git a/addons/crm/crm_meeting_view.xml b/addons/crm/crm_meeting_view.xml
index 34bdafe1c58..5d1bb3f29d7 100644
--- a/addons/crm/crm_meeting_view.xml
+++ b/addons/crm/crm_meeting_view.xml
@@ -148,6 +148,7 @@
@@ -214,10 +215,27 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -268,6 +286,10 @@
help="Current Meetings"/>
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm/crm_opportunity.py b/addons/crm/crm_opportunity.py
index 440033a2400..0adbf2f2489 100644
--- a/addons/crm/crm_opportunity.py
+++ b/addons/crm/crm_opportunity.py
@@ -143,9 +143,10 @@ class crm_opportunity(osv.osv):
context = {
'default_opportunity_id': opp.id,
'default_partner_id': opp.partner_id and opp.partner_id.id or False,
+ 'default_user_id': uid,
'default_section_id': opp.section_id and opp.section_id.id or False,
'default_email_from': opp.email_from,
- 'default_state': 'open',
+ 'default_state': 'open',
'default_name': opp.name
}
value = {
diff --git a/addons/crm/crm_opportunity_menu.xml b/addons/crm/crm_opportunity_menu.xml
index cb24990416f..36163abfa00 100644
--- a/addons/crm/crm_opportunity_menu.xml
+++ b/addons/crm/crm_opportunity_menu.xml
@@ -7,9 +7,9 @@
name="Phone calls"
res_model="crm.phonecall"
src_model="crm.lead"
- view_mode="calendar,tree,form"
+ view_mode="tree,calendar,form"
context="{'default_duration': 1.0 ,'default_opportunity_id': active_id}"
- domain="[('user_id','=',uid),('opportunity_id', '=', active_id)]"
+ domain="[('opportunity_id', '=', active_id)]"
view_type="form"/>
diff --git a/addons/crm/crm_opportunity_view.xml b/addons/crm/crm_opportunity_view.xml
index 550d82f86ac..4060337bdd7 100644
--- a/addons/crm/crm_opportunity_view.xml
+++ b/addons/crm/crm_opportunity_view.xml
@@ -258,6 +258,8 @@
+
+
- Outgoing Packings
+ Outgoing Ordersstock.pickingir.actions.act_windowform
From 08c25d2846b2bd1f1d218a64bb2f23ec1e03fb61 Mon Sep 17 00:00:00 2001
From: Fabien Pinckaers
Date: Thu, 24 Jun 2010 20:03:23 +0200
Subject: [PATCH 33/38] [IMP] better _-openerp__
bzr revid: fp@tinyerp.com-20100624180323-lzbvu2b1ctw65g28
---
addons/crm/crm_lead.py | 4 ++++
addons/crm/crm_lead_view.xml | 5 +++++
addons/marketing_campaign/__openerp__.py | 8 +++++++-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index dfa2a9b7009..e5e1c92daef 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -117,6 +117,8 @@ and users by email"),
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.lead')]"),
'partner_name': fields.char("Partner Name", size=64),
+ 'optin': fields.selection([('yes','Yes'),('no','No'),('unknown','/')],'Opt-In'),
+ 'optout': fields.selection([('yes','Yes'),('no','No'),('unknown','/')],'Opt-Out'),
'type':fields.selection([
('lead','Lead'),
('opportunity','Opportunity'),
@@ -148,6 +150,8 @@ and users by email"),
'user_id': crm_case._get_default_user,
'email_from': crm_case._get_default_email,
'state': lambda *a: 'draft',
+ 'optin': lambda *a: 'unknown',
+ 'optout': lambda *a: 'unknown',
'section_id': crm_case._get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index 9fe9b8e6170..0c5afb27739 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -146,6 +146,11 @@
+
+
+
+
+
diff --git a/addons/marketing_campaign/__openerp__.py b/addons/marketing_campaign/__openerp__.py
index 8abbf9eb706..16e45a36c96 100644
--- a/addons/marketing_campaign/__openerp__.py
+++ b/addons/marketing_campaign/__openerp__.py
@@ -28,9 +28,15 @@
"document",
"email_template"
],
- "author" : "Tiny",
+ "author" : "OpenERP SA",
"category": 'Generic Modules/Marketing',
"description": """
+Allows you to setup leads automation through marketing campaigns. The campaigns
+are dynamic and multi-channels. The process:
+* Design marketing campaigns that incluces mail templates, reports to print,
+ miscelleanous actions, etc.
+* Define segments that are selections of target people
+* Launch your campaign to automate communications.
""",
'website': 'http://www.openerp.com',
'init_xml': [],
From 22360362500dde60da75ed5722e98a98a6f4e2f5 Mon Sep 17 00:00:00 2001
From: Fabien Pinckaers
Date: Thu, 24 Jun 2010 20:11:45 +0200
Subject: [PATCH 34/38] fix
bzr revid: fp@tinyerp.com-20100624181145-aggkgnckrj2aem19
---
addons/sale/report/sale_report_view.xml | 120 ++++++++++++------------
1 file changed, 60 insertions(+), 60 deletions(-)
diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml
index c9aee627857..ae948f66b76 100644
--- a/addons/sale/report/sale_report_view.xml
+++ b/addons/sale/report/sale_report_view.xml
@@ -50,41 +50,41 @@
search
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
+
+
+
@@ -103,7 +103,7 @@
-
+
@@ -171,7 +171,7 @@
[('state','in',('manual','progress')),('date','<=', time.strftime('%Y-%m-%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]
-
+ graph
@@ -233,7 +233,7 @@
sale.reporttree
-
+
@@ -245,7 +245,7 @@
sale.reportgraph
-
+
@@ -253,11 +253,11 @@
- Sold product and Total Price
+ Sales by Productssale.reportformgraph,tree
- [('state','in',('manual','progress','done')),('date','<=', time.strftime('%Y-%m-%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]
+ [('state','in',('manual','progress','done')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]
@@ -273,29 +273,29 @@
-
- view.sales.by.month.tree
- sale.report
- tree
-
-
-
-
-
-
-
-
- Sales By Month
- sale.report
- form
- tree
- [('state','in',('manual','progress','done')),('year','=', time.strftime('%Y'))]
- {'group_by_no_leaf':1,'group_by':['month']}
-
-
-
+
+ view.sales.by.month.tree
+ sale.report
+ tree
+
+
+
+
+
+
+
+
+
+ Sales By Month
+ sale.report
+ form
+ tree,graph
+ [('state','in',('manual','progress','done')),('year','=', time.strftime('%Y'))]
+ {'group_by_no_leaf':1,'group_by':['month','product_id']}
+
+
-
+ sale.top.quotations.treesale.ordertree
@@ -317,7 +317,7 @@
formtree,form[('state','=','draft')]
-
+
From c0950423db6377a1e5b70208c32736825257c66f Mon Sep 17 00:00:00 2001
From: Fabien Pinckaers
Date: Thu, 24 Jun 2010 20:13:25 +0200
Subject: [PATCH 35/38] fix
bzr revid: fp@tinyerp.com-20100624181325-x44m1c6w5oj8kbm5
---
addons/marketing_campaign/marketing_campaign_view.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml
index f954d5de006..62cb4a9cf50 100644
--- a/addons/marketing_campaign/marketing_campaign_view.xml
+++ b/addons/marketing_campaign/marketing_campaign_view.xml
@@ -451,7 +451,7 @@
{'group_by': [], 'search_default_todo': 1}
-
+
Date: Thu, 24 Jun 2010 20:32:34 +0200
Subject: [PATCH 36/38] [fix] marketing campaigns
bzr revid: fp@tinyerp.com-20100624183234-dxdmvi91q0ooqulf
---
addons/email_template/email_template.py | 3 +--
addons/email_template/email_template_account.py | 7 +++++--
addons/email_template/email_template_view.xml | 12 +++++++-----
addons/marketing_campaign/marketing_campaign.py | 2 +-
4 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 9190bd17e2b..133a47563a8 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -108,8 +108,7 @@ class email_template(osv.osv):
'enforce_from_account':fields.many2one(
'email_template.account',
string="Enforce From Account",
- help="Emails will be sent only from this account(which are approved).",
- domain=[('state','=','approved')]),
+ help="Emails will be sent only from this account(which are approved)."),
'from_email' : fields.related('enforce_from_account', 'email_id',
type='char', string='From',),
'def_to':fields.char(
diff --git a/addons/email_template/email_template_account.py b/addons/email_template/email_template_account.py
index 52cff384ec4..9ea1cd5b8fa 100644
--- a/addons/email_template/email_template_account.py
+++ b/addons/email_template/email_template_account.py
@@ -97,9 +97,12 @@ class email_template_account(osv.osv):
context
)['name'],
'state':lambda * a:'draft',
+ 'smtpport':lambda *a:25,
+ 'smtpserver':lambda *a:'localhost',
+ 'company':lambda *a:'yes',
'user':lambda self, cursor, user, context:user,
- 'send_pref':lambda * a: 'html',
- 'smtptls':lambda * a:True,
+ 'send_pref':lambda *a: 'html',
+ 'smtptls':lambda *a:True,
}
_sql_constraints = [
diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml
index 12a24ca0934..1d574ff8675 100644
--- a/addons/email_template/email_template_view.xml
+++ b/addons/email_template/email_template_view.xml
@@ -56,14 +56,16 @@
-
-
+
+
-
-
-
+
+
+
+
+
diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py
index c36b669ffcc..386e5639dd7 100644
--- a/addons/marketing_campaign/marketing_campaign.py
+++ b/addons/marketing_campaign/marketing_campaign.py
@@ -79,7 +79,7 @@ you required for the campaign"),
('start', '=', True)])
if not act_ids :
raise osv.except_osv("Error", "There is no starting activitity in the campaign")
- act_ids = act_obj.search(cr, uid, [('id', 'in', actvity_idsf),
+ act_ids = act_obj.search(cr, uid, [('id', 'in', actvity_ids),
('type', '=', 'email')])
for activity in act_obj.browse(cr, uid, act_ids):
if not activity.email_template_id.enforce_from_account :
From 59f9be67f791f40d698b7183b81f7a7abc34a654 Mon Sep 17 00:00:00 2001
From: "Harry (OpenERP)"
Date: Fri, 25 Jun 2010 01:23:32 +0530
Subject: [PATCH 37/38] [IMP] mail_gateway: * introduce 'email.server.tools' to
process_email and send acknowdgement after creating new record * use
'email.server.tools' model in script and fetchmail module to processing email
* start mailgateway services on crm.lead, project.issue, hr.applicant
bzr revid: hmo@tinyerp.com-20100624195332-7qci6vrimvzple5w
---
addons/crm/__init__.py | 1 -
addons/crm/crm_lead.py | 127 ++++++
addons/crm/crm_mailgate.py | 172 --------
addons/crm/security/ir.model.access.csv | 3 +-
addons/fetchmail/__openerp__.py | 2 +-
addons/fetchmail/fetchmail.py | 410 +++---------------
addons/fetchmail/fetchmail_view.xml | 72 +--
addons/fetchmail/security/ir.model.access.csv | 2 -
addons/hr_recruitment/hr_recruitment.py | 130 ++++++
addons/mail_gateway/mail_gateway.py | 348 ++++++++++++++-
addons/mail_gateway/mail_gateway_view.xml | 73 +++-
.../openerp_mailgate/openerp_mailgate.py | 395 +----------------
addons/project_issue/project_issue.py | 127 ++++++
13 files changed, 880 insertions(+), 982 deletions(-)
delete mode 100644 addons/crm/crm_mailgate.py
diff --git a/addons/crm/__init__.py b/addons/crm/__init__.py
index 362444ccc2c..e56ed80af30 100644
--- a/addons/crm/__init__.py
+++ b/addons/crm/__init__.py
@@ -20,7 +20,6 @@
##############################################################################
import crm
-import crm_mailgate
import crm_action_rule
import crm_segmentation
import crm_meeting
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index e5e1c92daef..2700581d6cc 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -27,6 +27,9 @@ import time
import mx.DateTime
from tools.translate import _
from crm import crm_case
+import collections
+import binascii
+import tools
class crm_lead(osv.osv, crm_case):
""" CRM Lead Case """
@@ -235,6 +238,130 @@ and users by email"),
}
return value
+ def message_new(self, cr, uid, msg, context):
+ """
+ Automatically calls when new email message arrives
+
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks
+ """
+
+ mailgate_pool = self.pool.get('email.server.tools')
+
+ subject = msg.get('subject')
+ body = msg.get('body')
+ msg_from = msg.get('from')
+ priority = msg.get('priority')
+
+ vals = {
+ 'name': subject,
+ 'email_from': msg_from,
+ 'email_cc': msg.get('cc'),
+ 'description': body,
+ 'user_id': False,
+ }
+ if msg.get('priority', False):
+ vals['priority'] = priority
+
+ res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
+ if res:
+ vals.update(res)
+ res = self.create(cr, uid, vals, context)
+
+
+ attachents = msg.get('attachments', [])
+ for attactment in attachents or []:
+ data_attach = {
+ 'name': attactment,
+ 'datas':binascii.b2a_base64(str(attachents.get(attactment))),
+ 'datas_fname': attactment,
+ 'description': 'Mail attachment',
+ 'res_model': self._name,
+ 'res_id': res,
+ }
+ self.pool.get('ir.attachment').create(cr, uid, data_attach)
+
+ return res
+
+ def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
+ """
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of update mail’s IDs
+ """
+
+ if isinstance(ids, (str, int, long)):
+ ids = [ids]
+
+ msg_from = msg['from']
+ vals.update({
+ 'description': msg['body']
+ })
+ if msg.get('priority', False):
+ vals['priority'] = msg.get('priority')
+
+ maps = {
+ 'cost':'planned_cost',
+ 'revenue': 'planned_revenue',
+ 'probability':'probability'
+ }
+ vls = { }
+ for line in msg['body'].split('\n'):
+ line = line.strip()
+ res = tools.misc.command_re.match(line)
+ if res and maps.get(res.group(1).lower(), False):
+ key = maps.get(res.group(1).lower())
+ vls[key] = res.group(2).lower()
+
+ vals.update(vls)
+ res = self.write(cr, uid, ids, vals)
+ return res
+
+ def emails_get(self, cr, uid, ids, context=None):
+
+ """
+ Get Emails
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of email’s IDs
+ @param context: A standard dictionary for contextual values
+ """
+ res = {}
+
+ if isinstance(ids, (str, int, long)):
+ select = [long(ids)]
+ else:
+ select = ids
+
+ for thread in self.browse(cr, uid, select, context=context):
+ values = collections.defaultdict(set)
+
+ for message in thread.message_ids:
+ user_email = (message.user_id and message.user_id.address_id and message.user_id.address_id.email) or False
+ values['user_email'].add(user_email)
+ values['email_from'].add(message.email_from)
+ values['email_cc'].add(message.email_cc or False)
+ values['priority'] = thread.priority
+
+ res[thread.id] = dict((key,list(values[key])) for key, value in values.iteritems())
+
+ return res
+
+ def msg_send(self, cr, uid, id, *args, **argv):
+
+ """ Send The Message
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of email’s IDs
+ @param *args: Return Tuple Value
+ @param **args: Return Dictionary of Keyword Value
+ """
+ return True
+
crm_lead()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/crm/crm_mailgate.py b/addons/crm/crm_mailgate.py
deleted file mode 100644
index d39293e4716..00000000000
--- a/addons/crm/crm_mailgate.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-import time
-import re
-import os
-
-import binascii
-import mx.DateTime
-import base64
-
-from tools.translate import _
-
-import tools
-from osv import fields,osv,orm
-from osv.orm import except_orm
-import collections
-
-from tools import command_re
-
-class mailgate_thread(osv.osv):
- """ mailgate_thread """
- _name = "mailgate.thread"
- _inherit = "mailgate.thread"
-
- def message_new(self, cr, uid, msg, context):
- """
- Automatically calls when new email message arrives
-
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks
- """
-
- mailgate_pool = self.pool.get('email.server.tools')
-
- subject = msg.get('subject')
- body = msg.get('body')
- msg_from = msg.get('from')
- priority = msg.get('priority')
-
- vals = {
- 'name': subject,
- 'email_from': msg_from,
- 'email_cc': msg.get('cc'),
- 'description': body,
- 'user_id': False,
- }
- if msg.get('priority', False):
- vals['priority'] = priority
-
- res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
- if res:
- vals.update(res)
- res = self.create(cr, uid, vals, context)
- cases = self.browse(cr, uid, [res])
- self._history(cr, uid, cases, _('Receive'), history=True, details=body, email_from=msg_from, message_id=msg.get('id'))
-
- attachents = msg.get('attachments', [])
- for attactment in attachents or []:
- data_attach = {
- 'name': attactment,
- 'datas':binascii.b2a_base64(str(attachents.get(attactment))),
- 'datas_fname': attactment,
- 'description': 'Mail attachment',
- 'res_model': self._name,
- 'res_id': res,
- }
- self.pool.get('ir.attachment').create(cr, uid, data_attach)
-
- return res
-
- def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
- """
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of update mail’s IDs
- """
-
- if isinstance(ids, (str, int, long)):
- ids = [ids]
-
- msg_from = msg['from']
- vals.update({
- 'description': msg['body']
- })
- if msg.get('priority', False):
- vals['priority'] = msg.get('priority')
-
- maps = {
- 'cost':'planned_cost',
- 'revenue': 'planned_revenue',
- 'probability':'probability'
- }
- vls = { }
- for line in msg['body'].split('\n'):
- line = line.strip()
- res = command_re.match(line)
- if res and maps.get(res.group(1).lower(), False):
- key = maps.get(res.group(1).lower())
- vls[key] = res.group(2).lower()
-
- vals.update(vls)
- res = self.write(cr, uid, ids, vals)
- cases = self.browse(cr, uid, ids)
- message_id = context.get('references_id', False)
- self._history(cr, uid, cases, _('Receive'), history=True, details=msg['body'], email_from=msg_from, message_id=message_id)
- #getattr(self, act)(cr, uid, select)
- return res
-
- def emails_get(self, cr, uid, ids, context=None):
-
- """
- Get Emails
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of email’s IDs
- @param context: A standard dictionary for contextual values
- """
- res = {}
-
- if isinstance(ids, (str, int, long)):
- select = [long(ids)]
- else:
- select = ids
-
- for thread in self.browse(cr, uid, select, context=context):
- values = collections.defaultdict(set)
-
- for message in thread.message_ids:
- user_email = (message.user_id and message.user_id.address_id and message.user_id.address_id.email) or False
- values['user_email'].add(user_email)
- values['email_from'].add(message.email_from)
- values['email_cc'].add(message.email_cc or False)
-
- res[str(thread.id)] = dict((key,list(values[key])) for key, value in values.iteritems())
-
- return res
-
- def msg_send(self, cr, uid, id, *args, **argv):
-
- """ Send The Message
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of email’s IDs
- @param *args: Return Tuple Value
- @param **args: Return Dictionary of Keyword Value
- """
- return True
-
-mailgate_thread()
diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv
index 8befbd8c084..595e63736bf 100644
--- a/addons/crm/security/ir.model.access.csv
+++ b/addons/crm/security/ir.model.access.csv
@@ -30,9 +30,8 @@
"access_crm_lead2opportunity_partner","crm.lead2opportunity.partner","model_crm_lead2opportunity_partner","crm.group_crm_user",1,1,1,1
"access_crm_installer","crm.installer.rule","model_crm_installer","base.group_system",1,1,1,1
"access_crm_lead_forward_to_partner","crm.lead.forward.to.partner","model_crm_lead_forward_to_partner","crm.group_crm_user",1,1,1,1
-"access_mailgate_thread","mailgate.thread","model_mailgate_thread","crm.group_crm_user",1,1,1,1
"access_res_partner","res.partner.crm.user","base.model_res_partner","crm.group_crm_user",1,0,0,0
"access_res_partner_address","res.partner.address.crm.user","base.model_res_partner_address","crm.group_crm_user",1,0,0,0
"access_res_partner_category","res.partner.category.crm.user","base.model_res_partner_category","crm.group_crm_user",1,0,0,0
"mail_gateway_mailgate_message","mail_gateway.mailgate.message","mail_gateway.model_mailgate_message","crm.group_crm_user",1,1,1,1
-"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","model_mailgate_thread","crm.group_crm_user",1,1,1,1
+"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","mail_gateway.model_mailgate_thread","crm.group_crm_user",1,1,1,1
diff --git a/addons/fetchmail/__openerp__.py b/addons/fetchmail/__openerp__.py
index a1276c11643..31037cca63f 100644
--- a/addons/fetchmail/__openerp__.py
+++ b/addons/fetchmail/__openerp__.py
@@ -24,7 +24,7 @@
{
"name" : "Fetchmail Server",
"version" : "1.0",
- "depends" : ["base"],
+ "depends" : ["base", 'mail_gateway'],
"author" : "Tiny",
"description": """Fetchmail:
* Fetch email from Pop / IMAP server
diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py
index aa403cc0090..14981c13837 100644
--- a/addons/fetchmail/fetchmail.py
+++ b/addons/fetchmail/fetchmail.py
@@ -19,140 +19,57 @@
#
##############################################################################
-import os
-import re
import time
-import email
-import binascii
-import mimetypes
-
from imaplib import IMAP4
-from imaplib import IMAP4_SSL
-
+from imaplib import IMAP4_SSL
from poplib import POP3
from poplib import POP3_SSL
-from email.header import Header
-from email.header import decode_header
-
import netsvc
-from osv import osv
-from osv import fields
-from tools.translate import _
+from osv import osv, fields
logger = netsvc.Logger()
-def html2plaintext(html, body_id=None, encoding='utf-8'):
- ## (c) Fry-IT, www.fry-it.com, 2007
- ##
- ## download here: http://www.peterbe.com/plog/html2plaintext
- """ from an HTML text, convert the HTML to plain text.
- If @body_id is provided then this is the tag where the
- body (not necessarily ) starts.
- """
- try:
- from BeautifulSoup import BeautifulSoup, SoupStrainer, Comment
- except:
- return html
-
- urls = []
- if body_id is not None:
- strainer = SoupStrainer(id=body_id)
- else:
- strainer = SoupStrainer('body')
-
- soup = BeautifulSoup(html, parseOnlyThese=strainer, fromEncoding=encoding)
- for link in soup.findAll('a'):
- title = link.renderContents()
- for url in [x[1] for x in link.attrs if x[0]=='href']:
- urls.append(dict(url=url, tag=str(link), title=title))
-
- html = soup.__str__()
-
- url_index = []
- i = 0
- for d in urls:
- if d['title'] == d['url'] or 'http://'+d['title'] == d['url']:
- html = html.replace(d['tag'], d['url'])
- else:
- i += 1
- html = html.replace(d['tag'], '%s [%s]' % (d['title'], i))
- url_index.append(d['url'])
-
- html = html.replace('','*').replace('','*')
- html = html.replace('','*').replace('','*')
- html = html.replace('
','*').replace('
','*')
- html = html.replace('
','**').replace('
','**')
- html = html.replace('
','**').replace('
','**')
- html = html.replace('','/').replace('','/')
-
- # the only line breaks we respect is those of ending tags and
- # breaks
-
- html = html.replace('\n',' ')
- html = html.replace(' ', '\n')
- html = html.replace('
', '\n')
- html = html.replace('', '\n\n')
- html = re.sub(' ', '\n', html)
- html = html.replace(' ' * 2, ' ')
-
- # for all other tags we failed to clean up, just remove then and
- # complain about them on the stderr
- def desperate_fixer(g):
- #print >>sys.stderr, "failed to clean up %s" % str(g.group())
- return ' '
-
- html = re.sub('<.*?>', desperate_fixer, html)
-
- # lstrip all lines
- html = '\n'.join([x.lstrip() for x in html.splitlines()])
-
- for i, url in enumerate(url_index):
- if i == 0:
- html += '\n\n'
- html += '[%s] %s\n' % (i+1, url)
- return html
-
class email_server(osv.osv):
-
+
_name = 'email.server'
_description = "POP/IMAP Server"
-
+
_columns = {
- 'name':fields.char('Name', size=256, required=True, readonly=False),
- 'active':fields.boolean('Active', required=False),
+ 'name':fields.char('Name', size=256, required=True, readonly=False),
+ 'active':fields.boolean('Active', required=False),
'state':fields.selection([
- ('draft','Not Confirmed'),
- ('wating','Waiting for Verification'),
- ('done','Confirmed'),
- ],'State', select=True, readonly=True),
- 'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly',False)]}),
+ ('draft', 'Not Confirmed'),
+ ('wating', 'Waiting for Verification'),
+ ('done', 'Confirmed'),
+ ], 'State', select=True, readonly=True),
+ 'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}),
+ 'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly', False)]}),
'type':fields.selection([
- ('pop','POP Server'),
- ('imap','IMAP Server'),
- ],'State', select=True, readonly=False),
- 'is_ssl':fields.boolean('SSL ?', required=False),
- 'attach':fields.boolean('Add Attachments ?', required=False),
- 'date': fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}),
- 'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'note': fields.text('Description'),
- 'action_id':fields.many2one('ir.actions.server', 'Reply Email', required=False, domain="[('state','=','email')]"),
- 'object_id': fields.many2one('ir.model',"Model", required=True),
- 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly',False)]}, help="Priority between 0 to 10, select define the order of Processing"),
- 'user_id':fields.many2one('res.users', 'User', required=False),
+ ('pop', 'POP Server'),
+ ('imap', 'IMAP Server'),
+ ], 'State', select=True, readonly=False),
+ 'is_ssl':fields.boolean('SSL ?', required=False),
+ 'attach':fields.boolean('Add Attachments ?', required=False),
+ 'date': fields.date('Date', readonly=True, states={'draft':[('readonly', False)]}),
+ 'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}),
+ 'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly', False)]}),
+ 'note': fields.text('Description'),
+ 'action_id':fields.many2one('ir.actions.server', 'Reply Email', required=False, domain="[('state','=','email')]"),
+ 'object_id': fields.many2one('ir.model', "Model", required=True),
+ 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Priority between 0 to 10, select define the order of Processing"),
+ 'user_id':fields.many2one('res.users', 'User', required=False),
}
_defaults = {
- 'state': lambda *a: "draft",
- 'active': lambda *a: True,
- 'priority': lambda *a: 5,
- 'date': lambda *a: time.strftime('%Y-%m-%d'),
- 'user_id': lambda self, cr, uid, ctx: uid,
+ 'state': lambda *a: "draft",
+ 'active': lambda *a: True,
+ 'priority': lambda *a: 5,
+ 'date': lambda *a: time.strftime('%Y-%m-%d'),
+ 'user_id': lambda self, cr, uid, ctx: uid,
}
-
+
def check_duplicate(self, cr, uid, ids):
vals = self.read(cr, uid, ids, ['user', 'password'])[0]
cr.execute("select count(id) from email_server where user='%s' and password='%s'" % (vals['user'], vals['password']))
@@ -160,201 +77,41 @@ class email_server(osv.osv):
if res:
if res[0] > 1:
return False
- return True
+ return True
_constraints = [
(check_duplicate, 'Warning! Can\'t have duplicate server configuration!', ['user', 'password'])
]
-
+
def onchange_server_type(self, cr, uid, ids, server_type=False, ssl=False):
port = 0
if server_type == 'pop':
port = ssl and 995 or 110
elif server_type == 'imap':
port = ssl and 993 or 143
-
+
return {'value':{'port':port}}
-
- def _process_email(self, cr, uid, server, message, context={}):
- context.update({
- 'server_id':server.id
- })
- history_pool = self.pool.get('mail.server.history')
- msg_txt = email.message_from_string(message)
- message_id = msg_txt.get('Message-ID', False)
-
- msg = {}
- if not message_id:
- return False
-
- fields = msg_txt.keys()
-
- msg['id'] = message_id
- msg['message-id'] = message_id
-
- def _decode_header(txt):
- txt = txt.replace('\r', '')
- return ' '.join(map(lambda (x, y): unicode(x, y or 'ascii'), decode_header(txt)))
-
- if 'Subject' in fields:
- msg['subject'] = _decode_header(msg_txt.get('Subject'))
-
- if 'Content-Type' in fields:
- msg['content-type'] = msg_txt.get('Content-Type')
-
- if 'From' in fields:
- msg['from'] = _decode_header(msg_txt.get('From'))
-
- if 'Delivered-To' in fields:
- msg['to'] = _decode_header(msg_txt.get('Delivered-To'))
-
- if 'Cc' in fields:
- msg['cc'] = _decode_header(msg_txt.get('Cc'))
-
- if 'Reply-To' in fields:
- msg['reply'] = _decode_header(msg_txt.get('Reply-To'))
-
- if 'Date' in fields:
- msg['date'] = msg_txt.get('Date')
-
- if 'Content-Transfer-Encoding' in fields:
- msg['encoding'] = msg_txt.get('Content-Transfer-Encoding')
-
- if 'References' in fields:
- msg['references'] = msg_txt.get('References')
-
- if 'X-openerp-caseid' in fields:
- msg['caseid'] = msg_txt.get('X-openerp-caseid')
-
- if 'X-Priority' in fields:
- msg['priority'] = msg_txt.get('X-priority', '3 (Normal)').split(' ')[0]
-
- if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', None):
- encoding = msg_txt.get_content_charset()
- msg['body'] = msg_txt.get_payload(decode=True)
- if encoding:
- msg['body'] = msg['body'].decode(encoding).encode('utf-8')
-
- attachents = {}
- if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', None):
- body = ""
- counter = 1
- for part in msg_txt.walk():
- if part.get_content_maintype() == 'multipart':
- continue
-
- encoding = part.get_content_charset()
-
- if part.get_content_maintype()=='text':
- content = part.get_payload(decode=True)
- filename = part.get_filename()
- if filename :
- attachents[filename] = content
- else:
- if encoding:
- content = unicode(content, encoding)
- if part.get_content_subtype() == 'html':
- body = html2plaintext(content)
- elif part.get_content_subtype() == 'plain':
- body = content
- elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text':
- filename = part.get_filename();
- if filename :
- attachents[filename] = part.get_payload(decode=True)
- else:
- res = part.get_payload(decode=True)
- if encoding:
- res = res.decode(encoding).encode('utf-8')
-
- body += res
-
- msg['body'] = body
- msg['attachments'] = attachents
-
-
- res_id = False
- if msg.get('references', False):
- id = False
- ref = msg.get('references')
- if '\r\n' in ref:
- ref = msg.get('references').split('\r\n')
- else:
- ref = msg.get('references').split(' ')
-
- if ref:
- hids = history_pool.search(cr, uid, [('name','=',ref[0].strip())])
- if hids:
- id = hids[0]
- history = history_pool.browse(cr, uid, id)
- model_pool = self.pool.get(server.object_id.model)
- context.update({
- 'references_id':ref[0]
- })
- vals = {
-
- }
- if hasattr(model_pool, 'message_update'):
- model_pool.message_update(cr, uid, [history.res_id], vals, msg, context=context)
- else:
- logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_update is not define in model %s' % (model_pool._name))
- return False
- res_id = id
- else:
- model_pool = self.pool.get(server.object_id.model)
- if hasattr(model_pool, 'message_new'):
- res_id = model_pool.message_new(cr, uid, msg, context)
- else:
- logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_new is not define in model %s' % (model_pool._name))
- return False
-
- if server.attach:
- for attactment in attachents or []:
- data_attach = {
- 'name': attactment,
- 'datas':binascii.b2a_base64(str(attachents.get(attactment))),
- 'datas_fname': attactment,
- 'description': 'Mail attachment',
- 'res_model': server.object_id.model,
- 'res_id': res_id,
- }
- self.pool.get('ir.attachment').create(cr, uid, data_attach)
-
- if server.action_id:
- action_pool = self.pool.get('ir.actions.server')
- action_pool.run(cr, uid, [server.action_id.id], {'active_id':res_id, 'active_ids':[res_id]})
-
- res = {
- 'name': message_id,
- 'res_id': res_id,
- 'server_id': server.id,
- 'note': msg.get('body', msg.get('from')),
- 'ref_id':msg.get('references', msg.get('id')),
- 'type':server.type
- }
- his_id = history_pool.create(cr, uid, res)
-
- return res_id
def set_draft(self, cr, uid, ids, context={}):
self.write(cr, uid, ids , {'state':'draft'})
return True
-
+
def button_fetch_mail(self, cr, uid, ids, context={}):
self.fetch_mail(cr, uid, ids)
# sendmail_thread = threading.Thread(target=self.fetch_mail, args=(cr, uid, ids))
# sendmail_thread.start()
return True
-
+
def _fetch_mails(self, cr, uid, ids=False, context={}):
if not ids:
ids = self.search(cr, uid, [])
return self.fetch_mail(cr, uid, ids, context)
-
- def fetch_mail(self, cr, uid, ids, context={}):
+ def fetch_mail(self, cr, uid, ids, context={}):
+ email_tool = self.pool.get('email.server.tools')
for server in self.browse(cr, uid, ids, context):
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail start checking for new emails on %s' % (server.name))
-
+
count = 0
try:
if server.type == 'imap':
@@ -363,17 +120,21 @@ class email_server(osv.osv):
imap_server = IMAP4_SSL(server.server, int(server.port))
else:
imap_server = IMAP4(server.server, int(server.port))
-
+
imap_server.login(server.user, server.password)
imap_server.select()
result, data = imap_server.search(None, '(UNSEEN)')
for num in data[0].split():
result, data = imap_server.fetch(num, '(RFC822)')
- if self._process_email(cr, uid, server, data[0][1], context):
+ res_id = email_tool.process_email(cr, uid, server.object_id.model, data[0][1], attach=server.attach, server_id=server.id, server_type=server.type, context=context)
+ if res_id and server.action_id:
+ action_pool = self.pool.get('ir.actions.server')
+ action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
+
imap_server.store(num, '+FLAGS', '\\Seen')
- count += 1
+ count += 1
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch/process %s email(s) from %s' % (count, server.name))
-
+
imap_server.close()
imap_server.logout()
elif server.type == 'pop':
@@ -382,7 +143,7 @@ class email_server(osv.osv):
pop_server = POP3_SSL(server.server, int(server.port))
else:
pop_server = POP3(server.server, int(server.port))
-
+
#TODO: use this to remove only unread messages
#pop_server.user("recent:"+server.user)
pop_server.user(server.user)
@@ -393,79 +154,38 @@ class email_server(osv.osv):
for num in range(1, numMsgs + 1):
(header, msges, octets) = pop_server.retr(num)
msg = '\n'.join(msges)
- self._process_email(cr, uid, server, msg, context)
+ res_id = email_tool.process_email(cr, uid, server.object_id.model, data[0][1], attach=server.attach, server_id=server.id, server_type=server.type, context=context)
+ if res_id and server.action_id:
+ action_pool = self.pool.get('ir.actions.server')
+ action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
+
pop_server.dele(num)
pop_server.quit()
-
+
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch %s email(s) from %s' % (numMsgs, server.name))
-
+
self.write(cr, uid, [server.id], {'state':'done'})
except Exception, e:
logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (e))
-
+
return True
email_server()
-class mail_server_history(osv.osv):
+class mailgate_message(osv.osv):
+
+ _inherit = "mailgate.message"
- _name = "mail.server.history"
- _description = "Mail Server History"
-
_columns = {
- 'name': fields.char('Message Id', size=256, readonly=True, help="Message Id in Email Server.", select=True),
- 'ref_id': fields.char('Referance Id', size=256, readonly=True, help="Message Id in Email Server.", select=True),
- 'res_id': fields.integer("Resource ID", readonly=True, select=True),
- 'server_id': fields.many2one('email.server',"Mail Server", readonly=True, select=True),
- 'model_id':fields.related('server_id', 'object_id', type='many2one', relation='ir.model', string='Model', readonly=True, select=True),
- 'note': fields.text('Notes', readonly=True),
- 'create_date': fields.datetime('Created Date', readonly=True),
+ 'server_id': fields.many2one('email.server', "Mail Server", readonly=True, select=True),
'type':fields.selection([
- ('pop','POP Server'),
- ('imap','IMAP Server'),
- ],'State', select=True, readonly=True),
+ ('pop', 'POP Server'),
+ ('imap', 'IMAP Server'),
+ ], 'State', select=True, readonly=True),
}
_order = 'id desc'
-
-mail_server_history()
-class fetchmail_tool(osv.osv):
+mailgate_message()
- _name = 'email.server.tools'
- _description = "Email Tools"
- _auto = False
-
- def to_email(self, text):
- _email = re.compile(r'.*<.*@.*\..*>', re.UNICODE)
- def record(path):
- eml = path.group()
- index = eml.index('<')
- eml = eml[index:-1].replace('<','').replace('>','')
- return eml
-
- bits = _email.sub(record, text)
- return bits
-
- def get_partner(self, cr, uid, from_email, context=None):
- """
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks
- @param from_email: email address based on that function will search for the correct
- """
-
- res = {
- 'partner_address_id': False,
- 'partner_id': False
- }
- from_email = self.to_email(from_email)
- address_ids = self.pool.get('res.partner.address').search(cr, uid, [('email', '=', from_email)])
- if address_ids:
- address = self.pool.get('res.partner.address').browse(cr, uid, address_ids[0])
- res['partner_address_id'] = address_ids[0]
- res['partner_id'] = address.partner_id.id
-
- return res
-
-fetchmail_tool()
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/fetchmail/fetchmail_view.xml b/addons/fetchmail/fetchmail_view.xml
index 510b31b6131..330f9fec349 100644
--- a/addons/fetchmail/fetchmail_view.xml
+++ b/addons/fetchmail/fetchmail_view.xml
@@ -103,82 +103,54 @@
action="action_email_server_tree_imap"
/>
-
- mail.server.history.tree
- mail.server.history
- tree
-
-
-
-
-
-
-
-
-
-
-
- mail.server.history.form
- mail.server.history
- form
-
-
-
-
-
+
+
+
+
+ res_model="mailgate.message" src_model="email.server"/>
-
+ Received Email History
- mail.server.history
+ mailgate.messageformtree,form
-
+ {'type':'imap'}[('type','=','imap')]
+ id="menu_action_mailgate_message_tree"
+ action="action_mailgate_message_tree"/>
-
+ Received Email History
- mail.server.history
+ mailgate.messageformtree,form
-
+ {'type':'pop'}[('type','=','pop')]
+ id="menu_action_mailgate_message_tree_pop"
+ action="action_mailgate_message_tree_pop"/>
diff --git a/addons/fetchmail/security/ir.model.access.csv b/addons/fetchmail/security/ir.model.access.csv
index 907aa187913..7aa6c84d22b 100644
--- a/addons/fetchmail/security/ir.model.access.csv
+++ b/addons/fetchmail/security/ir.model.access.csv
@@ -1,4 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_email_server","email.server","model_email_server",,1,1,1,1
-"access_mail_server_history","mail.server.history","model_mail_server_history",,1,1,1,1
-"access_email_server_tools","email.server.tools","model_email_server_tools",,1,1,1,1
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py
index cf194a60bb4..9d6c2cfbbbb 100644
--- a/addons/hr_recruitment/hr_recruitment.py
+++ b/addons/hr_recruitment/hr_recruitment.py
@@ -21,6 +21,11 @@
from osv import fields, osv
from crm import crm
+import tools
+import collections
+import binascii
+import tools
+from tools.translate import _
AVAILABLE_STATES = [
('draft', 'New'),
@@ -240,6 +245,131 @@ class hr_applicant(osv.osv, crm.crm_case):
context.update({'survey_id': record.survey.id, 'response_id' : [record.response], 'response_no':0, })
value = self.pool.get("survey").action_print_survey(cr, uid, ids, context)
return value
+
+ def message_new(self, cr, uid, msg, context):
+ """
+ Automatically calls when new email message arrives
+
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks
+ """
+
+ mailgate_pool = self.pool.get('email.server.tools')
+
+ subject = msg.get('subject')
+ body = msg.get('body')
+ msg_from = msg.get('from')
+ priority = msg.get('priority')
+
+ vals = {
+ 'name': subject,
+ 'email_from': msg_from,
+ 'email_cc': msg.get('cc'),
+ 'description': body,
+ 'user_id': False,
+ }
+ if msg.get('priority', False):
+ vals['priority'] = priority
+
+ res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
+ if res:
+ vals.update(res)
+ res = self.create(cr, uid, vals, context)
+
+
+ attachents = msg.get('attachments', [])
+ for attactment in attachents or []:
+ data_attach = {
+ 'name': attactment,
+ 'datas':binascii.b2a_base64(str(attachents.get(attactment))),
+ 'datas_fname': attactment,
+ 'description': 'Mail attachment',
+ 'res_model': self._name,
+ 'res_id': res,
+ }
+ self.pool.get('ir.attachment').create(cr, uid, data_attach)
+
+ return res
+
+ def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
+ """
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of update mail’s IDs
+ """
+
+ if isinstance(ids, (str, int, long)):
+ ids = [ids]
+
+ msg_from = msg['from']
+ vals.update({
+ 'description': msg['body']
+ })
+ if msg.get('priority', False):
+ vals['priority'] = msg.get('priority')
+
+ maps = {
+ 'cost':'planned_cost',
+ 'revenue': 'planned_revenue',
+ 'probability':'probability'
+ }
+ vls = { }
+ for line in msg['body'].split('\n'):
+ line = line.strip()
+ res = tools.misc.command_re.match(line)
+ if res and maps.get(res.group(1).lower(), False):
+ key = maps.get(res.group(1).lower())
+ vls[key] = res.group(2).lower()
+
+ vals.update(vls)
+ res = self.write(cr, uid, ids, vals)
+ return res
+
+ def emails_get(self, cr, uid, ids, context=None):
+
+ """
+ Get Emails
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of email’s IDs
+ @param context: A standard dictionary for contextual values
+ """
+ res = {}
+
+ if isinstance(ids, (str, int, long)):
+ select = [long(ids)]
+ else:
+ select = ids
+
+ for thread in self.browse(cr, uid, select, context=context):
+ values = collections.defaultdict(set)
+
+ for message in thread.message_ids:
+ user_email = (message.user_id and message.user_id.address_id and message.user_id.address_id.email) or False
+ values['user_email'].add(user_email)
+ values['email_from'].add(message.email_from)
+ values['email_cc'].add(message.email_cc or False)
+ values['priority'] = thread.priority
+
+ res[thread.id] = dict((key,list(values[key])) for key, value in values.iteritems())
+
+ return res
+
+ def msg_send(self, cr, uid, id, *args, **argv):
+
+ """ Send The Message
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of email’s IDs
+ @param *args: Return Tuple Value
+ @param **args: Return Dictionary of Keyword Value
+ """
+ return True
+
hr_applicant()
class hr_job(osv.osv):
diff --git a/addons/mail_gateway/mail_gateway.py b/addons/mail_gateway/mail_gateway.py
index 7a7c00c9deb..edb723a3ba0 100644
--- a/addons/mail_gateway/mail_gateway.py
+++ b/addons/mail_gateway/mail_gateway.py
@@ -27,6 +27,7 @@ import email
from email.header import decode_header
import base64
import re
+from tools.translate import _
class mailgate_thread(osv.osv):
'''
@@ -37,14 +38,24 @@ class mailgate_thread(osv.osv):
_rec_name = 'thread'
_columns = {
- 'thread': fields.char('Thread', size=32, required=False),
- 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True)], readonly=True),
- 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False)], readonly=True),
- 'model': fields.char('Model Name', size=64, required=False),
- 'res_id': fields.integer('Resource ID'),
- }
-
- def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=None, context=None):
+ 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True)]),
+ 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False)]),
+ }
+
+ def message_new(self, cr, uid, msg, context):
+ raise Exception, _('Method is not implemented')
+
+ def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
+ raise Exception, _('Method is not implemented')
+
+ def emails_get(self, cr, uid, ids, context=None):
+ raise Exception, _('Method is not implemented')
+
+ def msg_send(self, cr, uid, id, *args, **argv):
+ raise Exception, _('Method is not implemented')
+
+ def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, \
+ email_from=False, message_id=False, references=None, attach=None, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@@ -98,6 +109,7 @@ class mailgate_thread(osv.osv):
(hasattr(case, 'user_id') and case.user_id and case.user_id.address_id and \
case.user_id.address_id.email) or tools.config.get('email_from', False),
'partner_id': hasattr(case, 'partner_id') and (case.partner_id and case.partner_id.id or False) or False,
+ 'references': references,
'message_id': message_id,
'attachment_ids': [(6, 0, attachments)]
}
@@ -125,7 +137,8 @@ class mailgate_message(osv.osv):
'email_to': fields.char('Email To', size=84),
'email_cc': fields.char('Email CC', size=84),
'email_bcc': fields.char('Email BCC', size=84),
- 'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email Server.", select=True),
+ 'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email.", select=True),
+ 'references': fields.text('References', readonly=True, help="Referencess emails."),
'description': fields.text('Description'),
'partner_id': fields.many2one('res.partner', 'Partner', required=False),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'),
@@ -133,3 +146,320 @@ class mailgate_message(osv.osv):
mailgate_message()
+class mailgate_tool(osv.osv_memory):
+
+ _name = 'email.server.tools'
+ _description = "Email Server Tools"
+
+ def _to_decode(self, s, charsets):
+ for charset in charsets:
+ if charset:
+ try:
+ return s.decode(charset)
+ except UnicodeError:
+ pass
+ return s.decode('latin1')
+
+ def _decode_header(self, text):
+ if text:
+ text = decode_header(text.replace('\r', ''))
+ return ''.join(map(lambda x:self._to_decode(x[0], [x[1]]), text or []))
+
+ def to_email(self, text):
+ _email = re.compile(r'.*<.*@.*\..*>', re.UNICODE)
+ def record(path):
+ eml = path.group()
+ index = eml.index('<')
+ eml = eml[index:-1].replace('<', '').replace('>', '')
+ return eml
+
+ bits = _email.sub(record, text)
+ return bits
+
+ def history(self, cr, uid, model, res_ids, msg, attach):
+ """This function creates history for mails fetched
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: OpenObject Model
+ @param res_ids: Ids of the record of OpenObject model created
+ @param msg: Email details
+ @param attach: Email attachments
+ """
+ if isinstance(res_ids, (int, long)):
+ res_ids = [res_ids]
+
+ msg_pool = self.pool.get('mailgate.message')
+ for res_id in res_ids:
+ msg_data = {
+ 'name': msg.get('subject', 'No subject'),
+ 'date': msg.get('date') ,
+ 'description': msg.get('body', msg.get('from')),
+ 'history': True,
+ 'res_model': model,
+ 'email_cc': msg.get('cc'),
+ 'email_from': msg.get('from'),
+ 'email_to': msg.get('to'),
+ 'message_id': msg.get('message-id'),
+ 'references': msg.get('references'),
+ 'res_id': res_id,
+ 'user_id': uid,
+ 'attachment_ids': [(6, 0, attach)]
+ }
+ msg_id = msg_pool.create(cr, uid, msg_data)
+ return True
+
+ def email_send(self, cr, uid, model, res_id, msg, from_email=False, email_default=False):
+ """This function Sends return email on submission of Fetched email in OpenERP database
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: OpenObject Model
+ @param res_id: Id of the record of OpenObject model created from the Email details
+ @param msg: Email details
+ @param email_default: Default Email address in case of any Problem
+ """
+ history_pool = self.pool.get('mailgate.message')
+ model_pool = self.pool.get(model)
+ from_email = from_email or tools.config.get('email_from', None)
+ message = email.message_from_string(str(msg))
+ subject = "[%s] %s" %(res_id, self._decode_header(message['Subject']))
+ #msg_mails = []
+ #mails = [self._decode_header(message['From']), self._decode_header(message['To'])]
+ #mails += self._decode_header(message.get('Cc', '')).split(',')
+
+ values = {}
+ if hasattr(model_pool, 'emails_get'):
+ values = model_pool.emails_get(cr, uid, [res_id])
+ emails = values.get(res_id, {})
+
+ priority = emails.get('priority', [3])[0]
+ em = emails['user_email'] + emails['email_from'] + emails['email_cc']
+ msg_mails = map(self.to_email, filter(None, em))
+
+ #mm = [self._decode_header(message['From']), self._decode_header(message['To'])]
+ #mm += self._decode_header(message.get('Cc', '')).split(',')
+
+ #msg_mails = map(self.to_email, filter(None, mm))
+
+ encoding = message.get_content_charset()
+ message['body'] = message.get_payload(decode=True)
+ if encoding:
+ message['body'] = tools.ustr(message['body'].decode(encoding))
+
+ body = _("""
+Hello %s,
+
+ Your Request ID: %s
+
+Thanks
+
+-------- Original Message --------
+%s
+""") %(self._decode_header(message['From']), res_id, message['body'])
+ res = None
+ try:
+ res = tools.email_send(from_email, msg_mails, subject, body, openobject_id=res_id)
+ except Exception, e:
+ if email_default:
+ temp_msg = '[%s] %s'%(res_id, self._decode_header(message['Subject']))
+ del message['Subject']
+ message['Subject'] = '[OpenERP-FetchError] %s' %(temp_msg)
+ tools.email_send(from_email, email_default, message.get('Subject'), message.get('body'), openobject_id=res_id)
+ return res
+
+ def process_email(self, cr, uid, model, message, attach=True, context=None):
+ """This function Processes email and create record for given OpenERP model
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: OpenObject Model
+ @param message: Email details
+ @param attach: Email attachments
+ @param context: A standard dictionary for contextual values"""
+
+ model_pool = self.pool.get(model)
+ if not context:
+ context = {}
+ res_id = False
+ # Create New Record into particular model
+ def create_record(msg):
+ if hasattr(model_pool, 'message_new'):
+ res_id = model_pool.message_new(cr, uid, msg, context)
+ else:
+ data = {
+ 'name': msg.get('subject'),
+ 'email_from': msg.get('from'),
+ 'email_cc': msg.get('cc'),
+ 'user_id': False,
+ 'description': msg.get('body'),
+ 'state' : 'draft',
+ }
+ data.update(self.get_partner(cr, uid, msg.get('from'), context=context))
+ res_id = model_pool.create(cr, uid, data, context=context)
+
+ att_ids = []
+ if attach:
+ for attachment in msg.get('attachments', []):
+ data_attach = {
+ 'name': attachment,
+ 'datas': binascii.b2a_base64(str(attachments.get(attachment))),
+ 'datas_fname': attachment,
+ 'description': 'Mail attachment',
+ 'res_model': model,
+ 'res_id': res_id,
+ }
+ att_ids.append(self.pool.get('ir.attachment').create(cr, uid, data_attach))
+
+ return res_id
+
+ history_pool = self.pool.get('mailgate.message')
+ msg_txt = email.message_from_string(message)
+ message_id = msg_txt.get('Message-ID', False)
+ msg = {}
+ if not message_id:
+ return False
+
+ fields = msg_txt.keys()
+ msg['id'] = message_id
+ msg['message-id'] = message_id
+
+ if 'Subject' in fields:
+ msg['subject'] = self._decode_header(msg_txt.get('Subject'))
+
+ if 'Content-Type' in fields:
+ msg['content-type'] = msg_txt.get('Content-Type')
+
+ if 'From' in fields:
+ msg['from'] = self._decode_header(msg_txt.get('From'))
+
+ if 'Delivered-To' in fields:
+ msg['to'] = self._decode_header(msg_txt.get('Delivered-To'))
+
+ if 'Cc' in fields:
+ msg['cc'] = self._decode_header(msg_txt.get('Cc'))
+
+ if 'Reply-To' in fields:
+ msg['reply'] = self._decode_header(msg_txt.get('Reply-To'))
+
+ if 'Date' in fields:
+ msg['date'] = msg_txt.get('Date')
+
+ if 'Content-Transfer-Encoding' in fields:
+ msg['encoding'] = msg_txt.get('Content-Transfer-Encoding')
+
+ if 'References' in fields:
+ msg['references'] = msg_txt.get('References')
+
+ if 'X-Priority' in fields:
+ msg['priority'] = msg_txt.get('X-priority', '3 (Normal)').split(' ')[0]
+
+ if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', None):
+ encoding = msg_txt.get_content_charset()
+ msg['body'] = msg_txt.get_payload(decode=True)
+ if encoding:
+ msg['body'] = tools.ustr(msg['body'])
+
+ attachments = {}
+ if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', None):
+ body = ""
+ counter = 1
+ for part in msg_txt.walk():
+ if part.get_content_maintype() == 'multipart':
+ continue
+
+ encoding = part.get_content_charset()
+
+ if part.get_content_maintype()=='text':
+ content = part.get_payload(decode=True)
+ filename = part.get_filename()
+ if filename :
+ attachments[filename] = content
+ else:
+ if encoding:
+ content = unicode(content, encoding)
+ if part.get_content_subtype() == 'html':
+ body = tools.html2plaintext(content)
+ elif part.get_content_subtype() == 'plain':
+ body = content
+ elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text':
+ filename = part.get_filename();
+ if filename :
+ attachments[filename] = part.get_payload(decode=True)
+ else:
+ res = part.get_payload(decode=True)
+ if encoding:
+ res = tools.ustr(res)
+
+ body += res
+
+ msg['body'] = body
+ msg['attachments'] = attachments
+ res_ids = []
+ new_res_id = False
+ if msg.get('references', False):
+ references = msg.get('references')
+ if '\r\n' in references:
+ references = msg.get('references').split('\r\n')
+ else:
+ references = msg.get('references').split(' ')
+ for ref in references:
+ ref = ref.strip()
+ res_id = tools.misc.reference_re.search(ref)
+ if res_id:
+ res_id = res_id.group(1)
+ else:
+ res_id = tools.misc.res_re.search(msg['subject'])
+ if res_id:
+ res_id = res_id.group(1)
+ if res_id:
+ res_id = int(res_id)
+ res_ids.append(res_id)
+ model_pool = self.pool.get(model)
+
+ vals = {}
+ if hasattr(model_pool, 'message_update'):
+ model_pool.message_update(cr, uid, [res_id], vals, msg, context=context)
+
+ if not len(res_ids):
+ new_res_id = create_record(msg)
+ res_ids = [new_res_id]
+ # Store messages
+ if hasattr(model_pool, '_history'):
+ model_pool._history(cr, uid, res_ids, _('Receive'), history=True,
+ subject = msg.get('subject'),
+ email = msg.get('to'),
+ details = msg.get('body'),
+ email_from = msg.get('from'),
+ message_id = msg.get('message-id'),
+ references = msg.get('references', False),
+ attach = msg.get('attachments', {}).items(),
+ context = {'model' : model})
+ else:
+ self.history(cr, uid, model, res_ids, msg, att_ids)
+ return new_res_id
+
+ def get_partner(self, cr, uid, from_email, context=None):
+ """This function returns partner Id based on email passed
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks
+ @param from_email: email address based on that function will search for the correct
+ """
+ address_pool = self.pool.get('res.partner.address')
+ res = {
+ 'partner_address_id': False,
+ 'partner_id': False
+ }
+ from_email = self.to_email(from_email)
+ address_ids = address_pool.search(cr, uid, [('email', '=', from_email)])
+ if address_ids:
+ address = address_pool.browse(cr, uid, address_ids[0])
+ res['partner_address_id'] = address_ids[0]
+ res['partner_id'] = address.partner_id.id
+
+ return res
+
+mailgate_tool()
+
+
diff --git a/addons/mail_gateway/mail_gateway_view.xml b/addons/mail_gateway/mail_gateway_view.xml
index 95dce8054c1..09a24f92772 100644
--- a/addons/mail_gateway/mail_gateway_view.xml
+++ b/addons/mail_gateway/mail_gateway_view.xml
@@ -29,7 +29,7 @@
-
+
@@ -76,28 +76,30 @@
form
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163,10 +165,33 @@
- Mailgateway Threads
- mailgate.thread
+ Mailgateway Threads
+ mailgate.thread
+ tree,form
+ form
+
+
+
+
+
+ tree
+
+
+
+
+
+ form
+
+
+
+
+
+
+ Mailgateway Messages
+ mailgate.messagetree,formform
+ [('history', '=', True)]
@@ -195,4 +220,4 @@
/>
-
\ No newline at end of file
+
diff --git a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
index b9a9eefee15..f294c819c78 100755
--- a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
+++ b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
@@ -21,104 +21,15 @@
#
###########################################################################################
-import re
-import smtplib
-import email
-import mimetypes
-from email.Header import decode_header
-from email.MIMEText import MIMEText
-import xmlrpclib
-import os
-import binascii
-import time
-import socket
import logging
-import sys
import optparse
-
-email_re = re.compile(r"([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6})")
-case_re = re.compile(r"\[([0-9]+)\]", re.UNICODE)
-command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE)
-reference_re = re.compile("<.*-openobject-(\\d+)@(.*)>", re.UNICODE)
-
-priorities = {
- '1': '1 (Highest)',
- '2': '2 (High)',
- '3': '3 (Normal)',
- '4': '4 (Low)',
- '5': '5 (Lowest)',
-}
-
-def html2plaintext(html, body_id=None, encoding='utf-8'):
- ## (c) Fry-IT, www.fry-it.com, 2007
- ##
- ## download here: http://www.peterbe.com/plog/html2plaintext
-
-
- """ from an HTML text, convert the HTML to plain text.
- If @body_id is provided then this is the tag where the
- body (not necessarily ) starts.
- """
- try:
- from BeautifulSoup import BeautifulSoup, SoupStrainer, Comment
- except:
- return html
-
- urls = []
- if body_id is not None:
- strainer = SoupStrainer(id=body_id)
- else:
- strainer = SoupStrainer('body')
-
- soup = BeautifulSoup(html, parseOnlyThese=strainer, fromEncoding=encoding)
- for link in soup.findAll('a'):
- title = unicode(link)
- for url in [x[1] for x in link.attrs if x[0]=='href']:
- urls.append(dict(url=url, tag=unicode(link), title=title))
-
- html = unicode(soup)
-
- url_index = []
- i = 0
- for d in urls:
- if d['title'] == d['url'] or 'http://'+d['title'] == d['url']:
- html = html.replace(d['tag'], d['url'])
- else:
- i += 1
- html = html.replace(d['tag'], '%s [%s]' % (d['title'], i))
- url_index.append(d['url'])
-
- html = html.replace('', '*').replace('', '*')
- html = html.replace('', '*').replace('', '*')
- html = html.replace('
', '*').replace('
', '*')
- html = html.replace('
', '**').replace('
', '**')
- html = html.replace('
', '**').replace('
', '**')
- html = html.replace('', '/').replace('', '/')
-
- # the only line breaks we respect is those of ending tags and
- # breaks
-
- html = html.replace('\n', ' ')
- html = html.replace(' ', '\n')
- html = html.replace('