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.

237 lines
8.7 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2015 Antiun Ingeniería S.L. (http://www.antiun.com)
  3. # © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
  4. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
  5. from openerp import exceptions
  6. from openerp.http import local_redirect, request, route
  7. from openerp.addons.mass_mailing.controllers.main import MassMailController
  8. from .. import exceptions as _ex
  9. class CustomUnsubscribe(MassMailController):
  10. def _mailing_list_contacts_by_email(self, email):
  11. """Gets the mailing list contacts by email.
  12. This should not be displayed to the final user if security validations
  13. have not been matched.
  14. """
  15. return request.env["mail.mass_mailing.contact"].sudo().search([
  16. ("email", "=", email),
  17. ("opt_out", "=", False),
  18. ("list_id.not_cross_unsubscriptable", "=", False),
  19. ])
  20. def unsubscription_reason(self, mailing_id, email, res_id, token,
  21. qcontext_extra=None):
  22. """Get the unsubscription reason form.
  23. :param mail.mass_mailing mailing_id:
  24. Mailing where the unsubscription is being processed.
  25. :param str email:
  26. Email to be unsubscribed.
  27. :param int res_id:
  28. ID of the unsubscriber.
  29. :param dict qcontext_extra:
  30. Additional dictionary to pass to the view.
  31. """
  32. values = self.unsubscription_qcontext(mailing_id, email, res_id, token)
  33. values.update(qcontext_extra or dict())
  34. return request.website.render(
  35. "mass_mailing_custom_unsubscribe.reason_form",
  36. values)
  37. def unsubscription_qcontext(self, mailing_id, email, res_id, token):
  38. """Get rendering context for unsubscription form.
  39. :param mail.mass_mailing mailing_id:
  40. Mailing where the unsubscription is being processed.
  41. :param str email:
  42. Email to be unsubscribed.
  43. :param int res_id:
  44. ID of the unsubscriber.
  45. """
  46. email_fname = origin_name = None
  47. domain = [("id", "=", res_id)]
  48. record_ids = request.env[mailing_id.mailing_model].sudo()
  49. if "email_from" in record_ids._fields:
  50. email_fname = "email_from"
  51. elif "email" in record_ids._fields:
  52. email_fname = "email"
  53. if not (email_fname and email):
  54. # Trying to unsubscribe without email? Bad boy...
  55. raise exceptions.AccessDenied()
  56. domain.append((email_fname, "ilike", email))
  57. # Search additional mailing lists for the unsubscriber
  58. additional_contacts = self._mailing_list_contacts_by_email(email)
  59. if record_ids._name == "mail.mass_mailing.contact":
  60. domain.append(
  61. ("list_id", "in", mailing_id.contact_list_ids.ids))
  62. # Unsubscription targets
  63. record_ids = record_ids.search(domain)
  64. if record_ids._name == "mail.mass_mailing.contact":
  65. additional_contacts -= record_ids
  66. if not record_ids:
  67. # Trying to unsubscribe with fake criteria? Bad boy...
  68. raise exceptions.AccessDenied()
  69. # Get data to identify the source of the unsubscription
  70. fnames = self.unsubscription_special_fnames(record_ids._name)
  71. first = record_ids[:1]
  72. contact_name = first[fnames.get("contact", "name")]
  73. origin_model_name = request.env["ir.model"].search(
  74. [("model", "=", first._name)]).name
  75. try:
  76. first = first[fnames["related"]]
  77. except KeyError:
  78. pass
  79. try:
  80. origin_name = first[fnames["origin"]]
  81. except KeyError:
  82. pass
  83. # Get available reasons
  84. reason_ids = (
  85. request.env["mail.unsubscription.reason"].search([]))
  86. return {
  87. "additional_contact_ids": additional_contacts,
  88. "contact_name": contact_name,
  89. "email": email,
  90. "mailing_id": mailing_id,
  91. "origin_model_name": origin_model_name,
  92. "origin_name": origin_name,
  93. "reason_ids": reason_ids,
  94. "record_ids": record_ids,
  95. "res_id": res_id,
  96. "token": token,
  97. }
  98. def unsubscription_special_fnames(self, model):
  99. """Define special field names to generate the unsubscription qcontext.
  100. :return dict:
  101. Special fields will depend on the model, so this method should
  102. return something like::
  103. {
  104. "related": "parent_id",
  105. "origin": "display_name",
  106. "contact": "contact_name",
  107. }
  108. Where:
  109. - ``model.name`` is the technical name of the model.
  110. - ``related`` indicates the name of a field in ``model.name`` that
  111. contains a :class:`openerp.fields.Many2one` field which is
  112. considered what the user is unsubscribing from.
  113. - ``origin``: is the name of the field that contains the name of
  114. what the user is unsubscribing from.
  115. - ``contact`` is the name of the field that contains the name of
  116. the user that is unsubscribing.
  117. Missing keys will mean that nothing special is required for that
  118. model and it will use the default values.
  119. """
  120. specials = {
  121. "mail.mass_mailing.contact": {
  122. "related": "list_id",
  123. "origin": "display_name",
  124. },
  125. "crm.lead": {
  126. "origin": "name",
  127. "contact": "contact_name",
  128. },
  129. "hr.applicant": {
  130. "related": "job_id",
  131. "origin": "name",
  132. },
  133. # In case you install OCA's event_registration_mass_mailing
  134. "event.registration": {
  135. "related": "event_id",
  136. "origin": "name",
  137. },
  138. }
  139. return specials.get(model, dict())
  140. @route(auth="public", website=True)
  141. def mailing(self, mailing_id, email=None, res_id=None, **post):
  142. """Display a confirmation form to get the unsubscription reason."""
  143. mailing = request.env["mail.mass_mailing"]
  144. path = "/page/mass_mailing_custom_unsubscribe.%s"
  145. good_token = mailing.hash_create(mailing_id, res_id, email)
  146. # Trying to unsubscribe with fake hash? Bad boy...
  147. if good_token and post.get("token") != good_token:
  148. return local_redirect(path % "failure")
  149. mailing = mailing.sudo().browse(mailing_id)
  150. contact = request.env["mail.mass_mailing.contact"].sudo()
  151. unsubscription = request.env["mail.unsubscription"].sudo()
  152. if not post.get("reason_id"):
  153. # We need to know why you leave, get to the form
  154. return self.unsubscription_reason(
  155. mailing, email, res_id, post.get("token"))
  156. # Save reason and details
  157. try:
  158. with request.env.cr.savepoint():
  159. records = unsubscription.create({
  160. "email": email,
  161. "unsubscriber_id": ",".join(
  162. (mailing.mailing_model, res_id)),
  163. "reason_id": int(post["reason_id"]),
  164. "details": post.get("details", False),
  165. "mass_mailing_id": mailing_id,
  166. })
  167. # Should provide details, go back to form
  168. except _ex.DetailsRequiredError:
  169. return self.unsubscription_reason(
  170. mailing, email, res_id, post.get("token"),
  171. {"error_details_required": True})
  172. # Unsubscribe from additional lists
  173. for key, value in post.iteritems():
  174. try:
  175. label, list_id = key.split(",")
  176. if label != "list_id":
  177. raise ValueError
  178. list_id = int(list_id)
  179. except ValueError:
  180. pass
  181. else:
  182. contact_id = contact.browse(int(value))
  183. if contact_id.list_id.id == list_id:
  184. contact_id.opt_out = True
  185. records += unsubscription.create({
  186. "email": email,
  187. "unsubscriber_id": ",".join((contact._name, value)),
  188. "reason_id": int(post["reason_id"]),
  189. "details": post.get("details", False),
  190. "mass_mailing_id": mailing_id,
  191. })
  192. # All is OK, unsubscribe
  193. result = super(CustomUnsubscribe, self).mailing(
  194. mailing_id, email, res_id, **post)
  195. records.write({"success": result.data == "OK"})
  196. # Redirect to the result
  197. return local_redirect(path % ("success" if result.data == "OK"
  198. else "failure"))