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.

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