You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

391 lines
16 KiB

9 years ago
9 years ago
9 years ago
  1. from lxml import etree
  2. from openerp import api, models, fields, SUPERUSER_ID
  3. from openerp.tools import email_split
  4. from openerp.tools.translate import _
  5. class wizard(models.TransientModel):
  6. _name = 'mail_move_message.wizard'
  7. def _model_selection(self):
  8. selection = []
  9. config_parameters = self.env['ir.config_parameter']
  10. model_names = config_parameters.get_param('mail_relocation_models')
  11. model_names = model_names.split(',') if model_names else []
  12. if 'default_message_id' in self.env.context:
  13. message = self.env['mail.message'].browse(self.env.context['default_message_id'])
  14. if message.model and message.model not in model_names:
  15. model_names.append(message.model)
  16. if message.moved_from_model and message.moved_from_model not in model_names:
  17. model_names.append(message.moved_from_model)
  18. if model_names:
  19. selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])]
  20. return selection
  21. @api.model
  22. def default_get(self, fields_list):
  23. res = super(wizard, self).default_get(fields_list)
  24. model_fields = self.fields_get()
  25. if model_fields['model']['selection']:
  26. res['model'] = model_fields['model']['selection'] and model_fields['model']['selection'][0][0]
  27. if 'message_id' in res:
  28. message = self.env['mail.message'].browse(res['message_id'])
  29. email_from = message.email_from
  30. parts = email_split(email_from.replace(' ',','))
  31. if parts:
  32. email = parts[0]
  33. name = email_from.find(email) != -1 and email_from[:email_from.index(email)].replace('"', '').replace('<', '').strip() or email_from
  34. else:
  35. name, email = email_from
  36. res['message_name_from'] = name
  37. res['message_email_from'] = email
  38. res['partner_id'] = message.author_id.id
  39. if message.author_id and self.env.uid not in [u.id for u in message.author_id.user_ids]:
  40. res['filter_by_partner'] = True
  41. if message.author_id and res.get('model'):
  42. res_id = self.env[res['model']].search([], order='id desc', limit=1)
  43. res['res_id'] = res_id and res_id[0].id
  44. config_parameters = self.env['ir.config_parameter']
  45. res['move_followers'] = config_parameters.get_param('mail_relocation_move_followers')
  46. res['uid'] = self.env.uid
  47. return res
  48. message_id = fields.Many2one('mail.message', string='Message')
  49. message_body = fields.Html(related='message_id.body', string='Message to move', readonly=True)
  50. message_from = fields.Char(related='message_id.email_from', string='From', readonly=True)
  51. message_subject = fields.Char(related='message_id.subject', string='Subject', readonly=True)
  52. message_moved_by_message_id = fields.Many2one('mail.message', related='message_id.moved_by_message_id', string='Moved with', readonly=True)
  53. message_moved_by_user_id = fields.Many2one('res.users', related='message_id.moved_by_user_id', string='Moved by', readonly=True)
  54. message_is_moved = fields.Boolean(string='Is Moved', related='message_id.is_moved', readonly=True)
  55. parent_id = fields.Many2one('mail.message', string='Search by name', )
  56. model = fields.Selection(_model_selection, string='Model')
  57. res_id = fields.Integer(string='Record')
  58. can_move = fields.Boolean('Can move', compute='get_can_move')
  59. move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place')
  60. partner_id = fields.Many2one('res.partner', string='Author')
  61. filter_by_partner = fields.Boolean('Filter Records by partner')
  62. message_email_from = fields.Char()
  63. message_name_from = fields.Char()
  64. # FIXME message_to_read should be True even if current message or any his childs are unread
  65. message_to_read = fields.Boolean(related='message_id.needaction')
  66. uid = fields.Integer()
  67. move_followers = fields.Boolean(
  68. 'Move Followers',
  69. help="Add followers of current record to a new record.\n"
  70. "You must use this option, if new record has restricted access.\n"
  71. "You can change default value for this option at Settings/System Parameters")
  72. @api.depends('message_id')
  73. @api.one
  74. def get_can_move(self):
  75. # message was not moved before OR message is a top message of previous move
  76. self.can_move = not self.message_id.moved_by_message_id or self.message_id.moved_by_message_id.id == self.message_id.id
  77. @api.onchange('move_back')
  78. def on_change_move_back(self):
  79. if not self.move_back:
  80. return
  81. self.parent_id = self.message_id.moved_from_parent_id
  82. model = self.message_id.moved_from_model
  83. if self.message_id.is_moved:
  84. self.model = model
  85. self.res_id = self.message_id.moved_from_res_id
  86. @api.onchange('parent_id', 'res_id', 'model')
  87. def update_move_back(self):
  88. model = self.message_id.moved_from_model
  89. self.move_back = self.parent_id == self.message_id.moved_from_parent_id \
  90. and self.res_id == self.message_id.moved_from_res_id \
  91. and (self.model == model or (not self.model and not model))
  92. @api.onchange('parent_id')
  93. def on_change_parent_id(self):
  94. if self.parent_id and self.parent_id.model:
  95. self.model = self.parent_id.model
  96. self.res_id = self.parent_id.res_id
  97. else:
  98. self.model = None
  99. self.res_id = None
  100. @api.onchange('model', 'filter_by_partner', 'partner_id')
  101. def on_change_partner(self):
  102. domain = {'res_id': [('id', '!=', self.message_id.res_id)]}
  103. if self.model and self.filter_by_partner and self.partner_id:
  104. fields = self.env[self.model].fields_get(False)
  105. contact_field = False
  106. for n, f in fields.iteritems():
  107. if f['type'] == 'many2one' and f['relation'] == 'res.partner':
  108. contact_field = n
  109. break
  110. if contact_field:
  111. domain['res_id'].append((contact_field, '=', self.partner_id.id))
  112. if self.model:
  113. res_id = self.env[self.model].search(domain['res_id'], order='id desc', limit=1)
  114. self.res_id = res_id and res_id[0].id
  115. else:
  116. self.res_id = None
  117. return {'domain': domain}
  118. @api.one
  119. def check_access(self):
  120. cr = self._cr
  121. uid = self.env.user.id
  122. operation = 'write'
  123. context = self._context
  124. if not ( self.model and self.res_id ):
  125. return True
  126. model_obj = self.pool[self.model]
  127. mids = model_obj.exists(cr, uid, [self.res_id])
  128. if hasattr(model_obj, 'check_mail_message_access'):
  129. model_obj.check_mail_message_access(cr, uid, mids, operation, context=context)
  130. else:
  131. self.pool['mail.thread'].check_mail_message_access(cr, uid, mids, operation, model_obj=model_obj, context=context)
  132. @api.multi
  133. def open_moved_by_message_id(self):
  134. message_id = None
  135. for r in self:
  136. message_id = r.message_moved_by_message_id.id
  137. return {
  138. 'type': 'ir.actions.act_window',
  139. 'res_model': 'mail_move_message.wizard',
  140. 'view_mode': 'form',
  141. 'view_type': 'form',
  142. 'views': [[False, 'form']],
  143. 'target': 'new',
  144. 'context': {'default_message_id': message_id},
  145. }
  146. @api.multi
  147. def move(self):
  148. for r in self:
  149. r.check_access()
  150. if not r.parent_id or not (r.parent_id.model == r.model and
  151. r.parent_id.res_id == r.res_id):
  152. #link with the first message of record
  153. parent = self.env['mail.message'].search([('model','=',r.model), ('res_id','=',r.res_id)], order='id', limit=1)
  154. r.parent_id = parent.id or None
  155. r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers)
  156. if not (r.model and r.res_id):
  157. r.message_id.needaction = False
  158. return {
  159. 'type': 'ir.actions.client',
  160. 'name': 'All messages',
  161. 'tag': 'reload',
  162. }
  163. return {
  164. 'name': _('Record'),
  165. 'view_type': 'form',
  166. 'view_mode': 'form',
  167. 'res_model': r.model,
  168. 'res_id': r.res_id,
  169. 'views': [(False, 'form')],
  170. 'type': 'ir.actions.act_window',
  171. }
  172. @api.one
  173. def delete(self):
  174. self.message_id.unlink()
  175. return {}
  176. @api.model
  177. def create_partner(self, message_id, relation, partner_id, message_name_from, message_email_from):
  178. model = self.env[relation]
  179. message = self.env['mail.message'].browse(message_id)
  180. if not partner_id and message_name_from:
  181. partner_id = self.env['res.partner'].with_context({'update_message_author': True}).create({
  182. 'name': message_name_from,
  183. 'email': message_email_from
  184. }).id
  185. context = {'partner_id': partner_id}
  186. if model._rec_name:
  187. context.update({'default_%s' % model._rec_name: message.subject})
  188. fields = model.fields_get()
  189. contact_field = False
  190. for n, f in fields.iteritems():
  191. if f['type'] == 'many2one' and f['relation'] == 'res.partner':
  192. contact_field = n
  193. break
  194. if contact_field:
  195. context.update({'default_%s' % contact_field: partner_id})
  196. return context
  197. @api.one
  198. def read_close(self):
  199. self.message_id.set_message_read(True)
  200. self.message_id.child_ids.set_message_read(True)
  201. return {'type': 'ir.actions.act_window_close'}
  202. class mail_message(models.Model):
  203. _inherit = 'mail.message'
  204. is_moved = fields.Boolean('Is moved')
  205. moved_from_res_id = fields.Integer('Related Document ID (Original)')
  206. moved_from_model = fields.Char('Related Document Model (Original)')
  207. moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null')
  208. moved_by_message_id = fields.Many2one('mail.message', 'Moved by message', ondelete='set null', help='Top message, that initate moving this message')
  209. moved_by_user_id = fields.Many2one('res.users', 'Moved by user', ondelete='set null')
  210. all_child_ids = fields.One2many('mail.message', string='All childs', compute='_get_all_childs', help='all childs, including subchilds')
  211. @api.one
  212. def _get_all_childs(self, include_myself=True):
  213. ids = []
  214. if include_myself:
  215. ids.append(self.id)
  216. while True:
  217. new_ids = self.search([('parent_id', 'in', ids), ('id', 'not in', ids)]).ids
  218. if new_ids:
  219. ids = ids + new_ids
  220. continue
  221. break
  222. moved_childs = self.search([('moved_by_message_id', '=', self.id)]).ids
  223. self.all_child_ids = ids + moved_childs
  224. @api.multi
  225. def move_followers(self, model, ids):
  226. fol_obj = self.env['mail.followers']
  227. for message in self:
  228. followers = fol_obj.sudo().search([('res_model', '=', message.model),
  229. ('res_id', '=', message.res_id)])
  230. for f in followers:
  231. self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids])
  232. @api.one
  233. def move(self, parent_id, res_id, model, move_back, move_followers=False):
  234. if parent_id == res_id:
  235. return
  236. vals = {}
  237. if move_back:
  238. # clear variables if we move everything back
  239. vals['is_moved'] = False
  240. vals['moved_by_user_id'] = None
  241. vals['moved_by_message_id'] = None
  242. vals['moved_from_res_id'] = None
  243. vals['moved_from_model'] = None
  244. vals['moved_from_parent_id'] = None
  245. else:
  246. vals['parent_id'] = parent_id
  247. vals['res_id'] = res_id
  248. vals['model'] = model
  249. vals['is_moved'] = True
  250. vals['moved_by_user_id'] = self.env.user.id
  251. vals['moved_by_message_id'] = self.id
  252. for r in self.all_child_ids:
  253. r_vals = vals.copy()
  254. if not r.is_moved:
  255. # moved_from_* variables contain not last, but original
  256. # reference
  257. r_vals['moved_from_parent_id'] = r.parent_id.id
  258. r_vals['moved_from_res_id'] = r.res_id
  259. r_vals['moved_from_model'] = r.model
  260. elif move_back:
  261. r_vals['parent_id'] = r.moved_from_parent_id.id
  262. r_vals['res_id'] = r.moved_from_res_id
  263. r_vals['model'] = r.moved_from_model
  264. print 'update message', r, r_vals
  265. if move_followers:
  266. r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id'))
  267. r.sudo().write(r_vals)
  268. r.attachment_ids.sudo().write({
  269. 'res_id': r_vals.get('res_id'),
  270. 'res_model': r_vals.get('model')
  271. })
  272. def name_get(self, cr, uid, ids, context=None):
  273. if not (context or {}).get('extended_name'):
  274. return super(mail_message, self).name_get(cr, uid, ids, context=context)
  275. if isinstance(ids, (list, tuple)) and not len(ids):
  276. return []
  277. if isinstance(ids, (long, int)):
  278. ids = [ids]
  279. reads = self.read(cr, uid, ids, ['record_name','model', 'res_id'], context=context)
  280. res = []
  281. for record in reads:
  282. name = record['record_name'] or ''
  283. extended_name = ' [%s] ID %s' % (record.get('model', 'UNDEF'), record.get('res_id', 'UNDEF'))
  284. res.append((record['id'], name + extended_name))
  285. return res
  286. def _message_read_dict(self, cr, uid, message, parent_id=False, context=None):
  287. res = super(mail_message, self)._message_read_dict(cr, uid, message, parent_id, context)
  288. res['is_moved'] = message.is_moved
  289. return res
  290. @api.multi
  291. def message_format(self):
  292. message_values = super(mail_message, self).message_format()
  293. message_index = {message['id']: message for message in message_values}
  294. for item in self:
  295. msg = message_index.get(item.id)
  296. if msg:
  297. msg['is_moved'] = item.is_moved
  298. return message_values
  299. class mail_move_message_configuration(models.TransientModel):
  300. _name = 'mail_move_message.config.settings'
  301. _inherit = 'res.config.settings'
  302. model_ids = fields.Many2many(comodel_name='ir.model', string='Models')
  303. move_followers = fields.Boolean('Move Followers')
  304. @api.model
  305. def get_default_move_message_configs(self, fields):
  306. config_parameters = self.env['ir.config_parameter']
  307. model_obj = self.env['ir.model']
  308. model_names = config_parameters.get_param('mail_relocation_models')
  309. if not model_names:
  310. return {}
  311. model_names = model_names.split(',')
  312. model_ids = model_obj.search([('model', 'in', model_names)])
  313. return {
  314. 'model_ids': [m.id for m in model_ids],
  315. 'move_followers': config_parameters.get_param('mail_relocation_move_followers')
  316. }
  317. @api.multi
  318. def set_move_message_configs(self):
  319. config_parameters = self.env['ir.config_parameter']
  320. model_names = ''
  321. for record in self:
  322. model_names = ','.join([m.model for m in record.model_ids])
  323. config_parameters.set_param('mail_relocation_models', model_names)
  324. config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '')
  325. class res_partner(models.Model):
  326. _inherit = 'res.partner'
  327. @api.model
  328. def create(self, vals):
  329. res = super(res_partner, self).create(vals)
  330. if 'update_message_author' in self.env.context and 'email' in vals:
  331. mail_message_obj = self.env['mail.message']
  332. # Escape special SQL characters in email_address to avoid invalid matches
  333. email_address = (vals['email'].replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_'))
  334. email_brackets = "<%s>" % email_address
  335. messages = mail_message_obj.search([
  336. '|',
  337. ('email_from', '=ilike', email_address),
  338. ('email_from', 'ilike', email_brackets),
  339. ('author_id', '=', False)
  340. ])
  341. if messages:
  342. messages.sudo().write({'author_id': res.id})
  343. return res