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.

402 lines
16 KiB

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