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

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
10 years ago
10 years ago
10 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