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.

531 lines
19 KiB

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