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.

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