Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/1523347/?format=api
{ "id": 1523347, "url": "http://patchwork.ozlabs.org/api/patches/1523347/?format=api", "web_url": "http://patchwork.ozlabs.org/project/patchwork/patch/20210901165756.181192-19-stephen@that.guru/", "project": { "id": 16, "url": "http://patchwork.ozlabs.org/api/projects/16/?format=api", "name": "Patchwork", "link_name": "patchwork", "list_id": "patchwork.lists.ozlabs.org", "list_email": "patchwork@lists.ozlabs.org", "web_url": "http://jk.ozlabs.org/projects/patchwork/", "scm_url": "git://github.com/getpatchwork/patchwork", "webscm_url": "https://github.com/getpatchwork/patchwork", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20210901165756.181192-19-stephen@that.guru>", "list_archive_url": null, "date": "2021-09-01T16:57:55", "name": "[RFC,v2,18/19] templates: Convert mail settings pages", "commit_ref": null, "pull_url": null, "state": "rfc", "archived": false, "hash": "98ec6437bf94c409f3f6fed9d45cf5d57c8cbd20", "submitter": { "id": 69991, "url": "http://patchwork.ozlabs.org/api/people/69991/?format=api", "name": "Stephen Finucane", "email": "stephen@that.guru" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/patchwork/patch/20210901165756.181192-19-stephen@that.guru/mbox/", "series": [ { "id": 260605, "url": "http://patchwork.ozlabs.org/api/series/260605/?format=api", "web_url": "http://patchwork.ozlabs.org/project/patchwork/list/?series=260605", "date": "2021-09-01T16:57:37", "name": "Integrate Bulma", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/260605/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/1523347/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/1523347/checks/", "tags": {}, "related": [ { "id": 1516046, "url": "http://patchwork.ozlabs.org/api/patches/1516046/?format=api", "web_url": "http://patchwork.ozlabs.org/project/patchwork/patch/20210811213705.36293-19-stephen@that.guru/", "msgid": "<20210811213705.36293-19-stephen@that.guru>", "list_archive_url": null, "date": "2021-08-11T21:37:04", "name": "[RFC,18/19] templates: Convert mail settings pages", "mbox": "http://patchwork.ozlabs.org/project/patchwork/patch/20210811213705.36293-19-stephen@that.guru/mbox/" } ], "headers": { "Return-Path": "\n <patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "patchwork@lists.ozlabs.org" ], "Delivered-To": [ "patchwork-incoming@bilbo.ozlabs.org", "patchwork@lists.ozlabs.org" ], "Authentication-Results": [ "ozlabs.org;\n\tdkim=fail reason=\"key not found in DNS\" header.d=that.guru\n header.i=@that.guru header.a=rsa-sha256 header.s=x header.b=nSSY5YIc;\n\tdkim-atps=neutral", "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=2404:9400:2:0:216:3eff:fee1:b9f1; helo=lists.ozlabs.org;\n envelope-from=patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=<UNKNOWN>)", "lists.ozlabs.org;\n\tdkim=fail reason=\"key not found in DNS\" header.d=that.guru\n header.i=@that.guru header.a=rsa-sha256 header.s=x header.b=nSSY5YIc;\n\tdkim-atps=neutral", "lists.ozlabs.org;\n spf=none (no SPF record) smtp.mailfrom=that.guru\n (client-ip=136.175.108.45; helo=mail-108-mta45.mxroute.com;\n envelope-from=stephen@that.guru; receiver=<UNKNOWN>)", "lists.ozlabs.org;\n dkim=fail reason=\"key not found in DNS\" header.d=that.guru\n header.i=@that.guru\n header.a=rsa-sha256 header.s=x header.b=nSSY5YIc;\n dkim-atps=neutral" ], "Received": [ "from lists.ozlabs.org (lists.ozlabs.org\n [IPv6:2404:9400:2:0:216:3eff:fee1:b9f1])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 4H09j84pbCz9sW8\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 2 Sep 2021 03:17:16 +1000 (AEST)", "from boromir.ozlabs.org (localhost [IPv6:::1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4H09j83HlZz307T\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 2 Sep 2021 03:17:16 +1000 (AEST)", "from mail-108-mta45.mxroute.com (mail-108-mta45.mxroute.com\n [136.175.108.45])\n (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n (No client certificate requested)\n by lists.ozlabs.org (Postfix) with ESMTPS id 4H09hm07ygz301j\n for <patchwork@lists.ozlabs.org>; Thu, 2 Sep 2021 03:16:55 +1000 (AEST)", "from filter004.mxroute.com ([149.28.56.236] filter004.mxroute.com)\n (Authenticated sender: mN4UYu2MZsgR)\n by mail-108-mta45.mxroute.com (ZoneMTA) with ESMTPSA id\n 17ba25cfed700074ba.001\n for <patchwork@lists.ozlabs.org>\n (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256);\n Wed, 01 Sep 2021 17:16:48 +0000" ], "X-Zone-Loop": "f4d8b5511ac6259a7d7f37174aa8cd19e2df13775832", "X-Originating-IP": "[149.28.56.236]", "DKIM-Signature": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru;\n s=x;\n h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:\n Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID:\n Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc\n :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe:\n List-Post:List-Owner:List-Archive;\n bh=TnDAee2tAMxj0TdMWyMaJyXYPKHhELrUyv/+CxTyKeY=; b=nSSY5YIcwx+E/RZ+vwaCbV16rS\n ToIIIrw30a4k7ipXls+pHQPPjJDQ67SU0So35d1vVs4HeNuegiDtgW1z/c+0khBjKV52+f0vu3yzC\n RUw4bAWgbV1NxXjv+G+jKnW5MKei9WX5m3pR2KCAoWyWW3ziM8L6XN1nwa3dnUcwJLoCb7Z6i0VCg\n pNMFQZJkvIy8mZMP+PxEkqkoFkaE6+ip7rBL/CiBjqfuHvpc4pcOrL5/tnaQojA1Q5OYvnk5pD9+1\n 22gTjFlIz8JEWVMZPhG4TizVVaCiOlPSFPETamry7fbwKFrxLTvSbN+VSB74S4KFhNDHybcRSYvHf\n jXB6K2cQ==;", "From": "Stephen Finucane <stephen@that.guru>", "To": "patchwork@lists.ozlabs.org", "Subject": "[RFC PATCH v2 18/19] templates: Convert mail settings pages", "Date": "Wed, 1 Sep 2021 17:57:55 +0100", "Message-Id": "<20210901165756.181192-19-stephen@that.guru>", "X-Mailer": "git-send-email 2.31.1", "In-Reply-To": "<20210901165756.181192-1-stephen@that.guru>", "References": "<20210901165756.181192-1-stephen@that.guru>", "MIME-Version": "1.0", "X-AuthUser": "stephen@that.guru", "X-BeenThere": "patchwork@lists.ozlabs.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "Patchwork development <patchwork.lists.ozlabs.org>", "List-Unsubscribe": "<https://lists.ozlabs.org/options/patchwork>,\n <mailto:patchwork-request@lists.ozlabs.org?subject=unsubscribe>", "List-Archive": "<http://lists.ozlabs.org/pipermail/patchwork/>", "List-Post": "<mailto:patchwork@lists.ozlabs.org>", "List-Help": "<mailto:patchwork-request@lists.ozlabs.org?subject=help>", "List-Subscribe": "<https://lists.ozlabs.org/listinfo/patchwork>,\n <mailto:patchwork-request@lists.ozlabs.org?subject=subscribe>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Errors-To": "patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org", "Sender": "\"Patchwork\"\n <patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>" }, "content": "This one is rather tricky. We do a major overhaul of URLs and general\nflow of confirmations, relying heavily on the messages framework to\navoid the need to have separate pages. Previously, configuring opt-in or\nopt-out of email involved the following:\n\n GET /mail/\n POST /mail/\n [some validation]\n POST /mail/optout/ # or optin\n\nIf the last step threw an error, we'd stay on '/mail/optout/' or\n'/mail/optin/' with a displayed error and tell the user to correct the\nmistake. Now, we simply do this:\n\n GET /mail/\n POST /mail/\n [some validation]\n GET /mail/<email>/\n POST /mail/<email>/\n\nError messages are propagated via the messages framework with all\nnon-field validation errors resulting in a redirect to the homepage.\n\nSigned-off-by: Stephen Finucane <stephen@that.guru>\n---\n .../templates/patchwork/confirm-error.html | 23 ---\n .../templates/patchwork/mail-configure.html | 70 ++++++++\n .../templates/patchwork/mail-settings.html | 106 +++++++----\n patchwork/templates/patchwork/mail.html | 37 ----\n .../templates/patchwork/optin-request.html | 53 ------\n patchwork/templates/patchwork/optin.html | 21 ---\n .../templates/patchwork/optout-request.html | 56 ------\n patchwork/templates/patchwork/optout.html | 25 ---\n .../patchwork/registration-confirm.html | 14 --\n patchwork/urls.py | 3 +-\n patchwork/views/mail.py | 168 +++++++++++-------\n patchwork/views/notification.py | 35 ++--\n patchwork/views/user.py | 56 +++---\n 13 files changed, 304 insertions(+), 363 deletions(-)\n delete mode 100644 patchwork/templates/patchwork/confirm-error.html\n create mode 100644 patchwork/templates/patchwork/mail-configure.html\n delete mode 100644 patchwork/templates/patchwork/mail.html\n delete mode 100644 patchwork/templates/patchwork/optin-request.html\n delete mode 100644 patchwork/templates/patchwork/optin.html\n delete mode 100644 patchwork/templates/patchwork/optout-request.html\n delete mode 100644 patchwork/templates/patchwork/optout.html\n delete mode 100644 patchwork/templates/patchwork/registration-confirm.html", "diff": "diff --git patchwork/templates/patchwork/confirm-error.html patchwork/templates/patchwork/confirm-error.html\ndeleted file mode 100644\nindex b1ce42ee..00000000\n--- patchwork/templates/patchwork/confirm-error.html\n+++ /dev/null\n@@ -1,23 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% block title %}Confirmation{% endblock %}\n-{% block heading %}Confirmation{% endblock %}\n-\n-\n-{% block body %}\n-\n-{% if error == 'inactive' %}\n-<p>\n- This confirmation has already been processed; you've probably visited this\n- page before.\n-</p>\n-{% endif %}\n-\n-{% if error == 'expired' %}\n-<p>\n- The confirmation has expired. If you'd still like to perform the\n- {{ conf.get_type_display }} process, you'll need to resubmit the request.\n-</p>\n-{% endif %}\n-\n-{% endblock %}\ndiff --git patchwork/templates/patchwork/mail-configure.html patchwork/templates/patchwork/mail-configure.html\nnew file mode 100644\nindex 00000000..c0e07154\n--- /dev/null\n+++ patchwork/templates/patchwork/mail-configure.html\n@@ -0,0 +1,70 @@\n+{% extends \"base2.html\" %}\n+\n+{% block title %}Mail settings{% endblock %}\n+\n+{% block body %}\n+{% for message in messages %}\n+{% if message.tags == 'success' %}\n+<div class=\"notification is-success\">\n+{% elif message.tags == 'warning' %}\n+<div class=\"notification is-warning\">\n+{% elif message.tags == 'error' %}\n+<div class=\"notification is-danger\">\n+{% else %}\n+<div class=\"notification\">\n+{% endif %}\n+ {{ message }}\n+ <button class=\"delete\" onclick=\"dismiss(this);\"></button>\n+</div>\n+{% endfor %}\n+\n+<div class=\"container\" style=\"margin-top: 1rem;\">\n+ <section class=\"block\">\n+ <h1 class=\"title\">\n+ Mail settings for {{ email }}\n+ </h1>\n+ </section>\n+\n+ <div class=\"content\">\n+ <p>\n+ You can configure your notification settings for Patchwork.\n+ Use this if you wish to opt out of all email from Patchwork,\n+ or if have previously opted out of Patchwork mail but now wish to\n+ receive notifications from Patchwork.\n+ </p>\n+ <p>\n+ If you opt out of email, Patchwork may still email you if you do certain\n+ actions yourself (such as create a new Patchwork account) but will not\n+ send you unsolicited email.\n+ </p>\n+ <p>\n+ When you submit a request, an email will be sent to your address with\n+ a link to click to finalise the request.\n+ Patchwork does this to prevent someone modifying your mail settings\n+ without your consent.\n+ </p>\n+\n+{% if is_optout %}\n+ <p>\n+ Patchwork <strong>may not</strong> send automated notifications to this address.\n+ </p>\n+{% else %}\n+ <p>\n+ Patchwork <strong>may</strong> send automated notifications to this address.\n+ </p>\n+{% endif %}\n+\n+ <form class=\"form\" method=\"post\">\n+ {% csrf_token %}\n+ <div class=\"field is-grouped\">\n+ <div class=\"control\">\n+ <input class=\"button\" type=\"submit\" name=\"optin\" value=\"Opt-in\"{% if not is_optout %} disabled{% endif %}/>\n+ </div>\n+ <div class=\"control\">\n+ <input class=\"button\" type=\"submit\" name=\"optout\" value=\"Opt-out\"{% if is_optout %} disabled{% endif %}/>\n+ </div>\n+ </div>\n+ </form>\n+ </div>\n+</div>\n+{% endblock %}\ndiff --git patchwork/templates/patchwork/mail-settings.html patchwork/templates/patchwork/mail-settings.html\nindex 58f567ac..858140ae 100644\n--- patchwork/templates/patchwork/mail-settings.html\n+++ patchwork/templates/patchwork/mail-settings.html\n@@ -1,37 +1,83 @@\n-{% extends \"base.html\" %}\n+{% extends \"base2.html\" %}\n \n {% block title %}Mail settings{% endblock %}\n-{% block heading %}Mail settings{% endblock %}\n \n {% block body %}\n-<p>Settings for <strong>{{ email }}</strong>:</p>\n-\n-<table class=\"horizontal\">\n- <tr>\n- <th>Opt-out list</th>\n-{% if is_optout %}\n- <td>\n- Patchwork <strong>may not</strong> send automated notifications to this address.\n- </td>\n- <td>\n- <form method=\"post\" action=\"{% url 'mail-optin' %}\">\n- {% csrf_token %}\n- <input type=\"hidden\" name=\"email\" value=\"{{ email }}\"/>\n- <input type=\"submit\" value=\"Opt-in\"/>\n- </form>\n- </td>\n+{% for message in messages %}\n+{% if message.tags == 'success' %}\n+<div class=\"notification is-success\">\n+{% elif message.tags == 'warning' %}\n+<div class=\"notification is-warning\">\n+{% elif message.tags == 'error' %}\n+<div class=\"notification is-danger\">\n {% else %}\n- <td>\n- Patchwork <strong>may</strong> send automated notifications to this address.\n- </td>\n- <td>\n- <form method=\"post\" action=\"{% url 'mail-optout' %}\">\n- {% csrf_token %}\n- <input type=\"hidden\" name=\"email\" value=\"{{ email }}\"/>\n- <input type=\"submit\" value=\"Opt-out\"/>\n- </form>\n- </td>\n+<div class=\"notification\">\n {% endif %}\n- </tr>\n-</table>\n+ {{ message }}\n+ <button class=\"delete\" onclick=\"dismiss(this);\"></button>\n+</div>\n+{% endfor %}\n+\n+<div class=\"container\" style=\"margin-top: 1rem;\">\n+ <section class=\"block\">\n+ <h1 class=\"title\">\n+ Mail settings\n+ </h1>\n+ </section>\n+\n+ <p>\n+ You can configure Patchwork to send you mail on certain events,\n+ or block automated mail altogether. Enter your email address to\n+ view or change your email settings.\n+ </p>\n+\n+ <div class=\"block\"></div>\n+\n+{% if form.non_field_errors %}\n+ <div class=\"notification is-danger is-light\">\n+ <button class=\"delete\" onclick=\"dismiss(this);\"></button>\n+ {{ form.non_field_errors }}\n+ </div>\n+{% endif %}\n+\n+ <form class=\"form\" method=\"post\">\n+ {% csrf_token %}\n+\n+ <div class=\"field is-horizontal\">\n+ <div class=\"field-label is-normal\">\n+ <label for=\"email\" class=\"label\">\n+ Email address\n+ </label>\n+ </div>\n+ <div class=\"field-body\">\n+ <div class=\"field\">\n+ <div class=\"control\">\n+ <input id=\"id_email\" type=\"email\" name=\"email\" class=\"input\" placeholder=\"e.g. bobsmith@example.com\">\n+ </div>\n+ <p class=\"help\">\n+ Your email address\n+ </p>\n+{% for error in form.email.errors %}\n+ <p class=\"help is-danger\">{{ error }}</p>\n+{% endfor %}\n+ </div>\n+ </div>\n+ </div>\n+\n+ <div class=\"field is-horizontal\">\n+ <div class=\"field-label\">\n+ <!-- Left empty for spacing -->\n+ </div>\n+ <div class=\"field-body\">\n+ <div class=\"field\">\n+ <div class=\"control\">\n+ <button class=\"button is-primary\">\n+ Submit\n+ </button>\n+ </div>\n+ </div>\n+ </div>\n+ </div>\n+ </form>\n+</div>\n {% endblock %}\ndiff --git patchwork/templates/patchwork/mail.html patchwork/templates/patchwork/mail.html\ndeleted file mode 100644\nindex a2ad23d1..00000000\n--- patchwork/templates/patchwork/mail.html\n+++ /dev/null\n@@ -1,37 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% block title %}Mail settings{% endblock %}\n-{% block heading %}Mail settings{% endblock %}\n-\n-{% block body %}\n-<p>\n- You can configure Patchwork to send you mail on certain events,\n- or block automated mail altogether. Enter your email address to\n- view or change your email settings.\n-</p>\n-\n-<form method=\"post\">\n-{% csrf_token %}\n-<table class=\"form registerform\">\n-{% if form.errors %}\n- <tr>\n- <td colspan=\"2\" class=\"error\">\n- There was an error accessing your mail settings:\n- </td>\n- </tr>\n-{% endif %}\n- <tr>\n- <th>{{ form.email.label_tag }}</th>\n- <td>\n- {{ form.email }}\n- {{ form.email.errors }}\n- </td>\n- </tr>\n- <tr>\n- <td colspan=\"2\" class=\"submitrow\">\n- <input type=\"submit\" value=\"Access mail settings\"/>\n- </td>\n- </tr>\n-</table>\n-</form>\n-{% endblock %}\ndiff --git patchwork/templates/patchwork/optin-request.html patchwork/templates/patchwork/optin-request.html\ndeleted file mode 100644\nindex 36744c26..00000000\n--- patchwork/templates/patchwork/optin-request.html\n+++ /dev/null\n@@ -1,53 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% load admins %}\n-\n-{% block title %}Opt-in{% endblock %}\n-{% block heading %}Opt-in{% endblock %}\n-\n-{% block body %}\n-{% if confirmation %}\n-<p><strong>Opt-in confirmation email sent</strong></p>\n-<p>\n- An opt-in confirmation mail has been sent to\n- <strong>{{ confirmation.email }}</strong>, containing a link. Please click on\n- that link to confirm your opt-in.\n-</p>\n-{% else %}\n-{% if error %}\n-<p class=\"error\">{{ error }}</p>\n-{% endif %}\n-\n-{% if form %}\n-<p>\n- This form allows you to opt-in to automated email from Patchwork. Use\n- this if you have previously opted-out of Patchwork mail, but now want to\n- received notifications from Patchwork.\n-</p>\n-<p>\n- When you submit it, an email will be sent to your address with a link to\n- click to finalise the opt-in. Patchwork does this to prevent someone opting\n- you in without your consent.\n-</p>\n-\n-<form method=\"post\" action=\"\">\n- {% csrf_token %}\n- {{ form.email.errors }}\n- <div style=\"padding: 0.5em 1em 2em;\">\n- {{ form.email.label_tag }}: {{ form.email }}\n- </div>\n- <input type=\"submit\" value=\"Send me an opt-in link\">\n-</form>\n-{% endif %}\n-\n-{% if error and admins %}\n-<p>\n- If you are having trouble opting in, please email {% site_admins %}.\n-</p>\n-{% endif %}\n-{% endif %}\n-\n-{% if user.is_authenticated %}\n-<p>Return to your <a href=\"{% url 'user-profile' %}\">user profile</a>.</p>\n-{% endif %}\n-{% endblock %}\ndiff --git patchwork/templates/patchwork/optin.html patchwork/templates/patchwork/optin.html\ndeleted file mode 100644\nindex 659bfccb..00000000\n--- patchwork/templates/patchwork/optin.html\n+++ /dev/null\n@@ -1,21 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% block title %}Opt-in{% endblock %}\n-{% block heading %}Opt-in{% endblock %}\n-\n-{% block body %}\n-<p>\n- <strong>Opt-in complete</strong>. You have successfully opted back in to\n- automated email from this Patchwork system, using the address\n- <strong>{{ email }}</strong>.\n-</p>\n-<p>\n- If you later decide that you no longer want to receive automated mail from\n- Patchwork, just visit\n- <a href=\"{% url 'mail-settings' %}\">http://{{ site.domain }}{% url 'mail-settings' %}</a>,\n- or visit the main Patchwork page and navigate from there.\n-</p>\n-{% if user.is_authenticated %}\n-<p>Return to your <a href=\"{% url 'user-profile' %}\">user profile</a>.</p>\n-{% endif %}\n-{% endblock %}\ndiff --git patchwork/templates/patchwork/optout-request.html patchwork/templates/patchwork/optout-request.html\ndeleted file mode 100644\nindex a89f72bb..00000000\n--- patchwork/templates/patchwork/optout-request.html\n+++ /dev/null\n@@ -1,56 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% load admins %}\n-\n-{% block title %}Opt-out{% endblock %}\n-{% block heading %}Opt-out{% endblock %}\n-\n-{% block body %}\n-{% if confirmation %}\n-<p><strong>Opt-out confirmation email sent</strong></p>\n-<p>\n- An opt-out confirmation mail has been sent to\n- <strong>{{ confirmation.email }}</strong>, containing a link. Please click on\n- that link to confirm your opt-out.\n-</p>\n-{% else %}\n-{% if error %}\n-<p class=\"error\">{{ error }}</p>\n-{% endif %}\n-\n-{% if form %}\n-<p>\n- This form allows you to opt-out of automated email from Patchwork.\n-</p>\n-<p>\n- If you opt-out of email, Patchwork may still email you if you do certain\n- actions yourself (such as create a new Patchwork account), but will not\n- send you unsolicited email.\n-</p>\n-<p>\n- When you submit it, one email will be sent to your address with a link to\n- click to finalise the opt-out. Patchwork does this to prevent someone\n- opting you out without your consent.\n-</p>\n-<form method=\"post\" action=\"\">\n- {% csrf_token %}\n- {{ form.email.errors }}\n- <div style=\"padding: 0.5em 1em 2em;\">\n- {{ form.email.label_tag }}: {{ form.email }}\n- </div>\n- <input type=\"submit\" value=\"Send me an opt-out link\">\n-</form>\n-{% endif %}\n-\n-{% if error and admins %}\n-<p>\n- If you are having trouble opting out, please email {% site_admins %}.\n-</p>\n-{% endif %}\n-{% endif %}\n-\n-{% if user.is_authenticated %}\n-<p>Return to your <a href=\"{% url 'user-profile' %}\">user profile</a>.</p>\n-{% endif %}\n-\n-{% endblock %}\ndiff --git patchwork/templates/patchwork/optout.html patchwork/templates/patchwork/optout.html\ndeleted file mode 100644\nindex 2d7e67e5..00000000\n--- patchwork/templates/patchwork/optout.html\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% block title %}Opt-out{% endblock %}\n-{% block heading %}Opt-out{% endblock %}\n-\n-{% block body %}\n-<p>\n- <strong>Opt-out complete</strong>. You have successfully opted-out of\n- automated notifications from this Patchwork system, from the address\n- <strong>{{ email }}</strong>\n-</p>\n-<p>\n- Please note that you may still receive email from other Patchwork setups at\n- different sites, as they are run independently. You may need to opt-out of\n- those separately.\n-</p>\n-<p>\n- If you later decide to receive mail from Patchwork, just visit\n- <a href=\"{% url 'mail-settings' %}\">http://{{ site.domain }}{% url 'mail-settings' %}</a>,\n- or visit the main Patchwork page and navigate from there.\n-</p>\n-{% if user.is_authenticated %}\n-<p>Return to your <a href=\"{% url 'user-profile' %}\">user profile</a>.</p>\n-{% endif %}\n-{% endblock %}\ndiff --git patchwork/templates/patchwork/registration-confirm.html patchwork/templates/patchwork/registration-confirm.html\ndeleted file mode 100644\nindex e9219a5a..00000000\n--- patchwork/templates/patchwork/registration-confirm.html\n+++ /dev/null\n@@ -1,14 +0,0 @@\n-{% extends \"base.html\" %}\n-\n-{% block title %}Registration{% endblock %}\n-{% block heading %}Registration{% endblock %}\n-\n-{% block body %}\n-<p>Registration confirmed!</p>\n-\n-<p>\n- Your Patchwork registration is complete. Head over to your\n- <a href=\"{% url 'user-profile' %}\">profile</a> to start using\n- Patchwork's extra features.\n-</p>\n-{% endblock %}\ndiff --git patchwork/urls.py patchwork/urls.py\nindex 30c070a9..e8ddba10 100644\n--- patchwork/urls.py\n+++ patchwork/urls.py\n@@ -185,8 +185,7 @@ urlpatterns = [\n path('delegate/', api_views.delegates, name='api-delegates'),\n # email setup\n path('mail/', mail_views.settings, name='mail-settings'),\n- path('mail/optout/', mail_views.optout, name='mail-optout'),\n- path('mail/optin/', mail_views.optin, name='mail-optin'),\n+ path('mail/<path:email>/', mail_views.configure, name='mail-configure'),\n # about\n path('about/', about_views.about, name='about'),\n # legacy redirects\ndiff --git patchwork/views/mail.py patchwork/views/mail.py\nindex 1a2019eb..d20f9d2f 100644\n--- patchwork/views/mail.py\n+++ patchwork/views/mail.py\n@@ -7,6 +7,7 @@ import smtplib\n \n from django.conf import settings as conf_settings\n from django.core.mail import send_mail\n+from django.contrib import messages\n from django.http import HttpResponseRedirect\n from django.shortcuts import render\n from django.template.loader import render_to_string\n@@ -22,12 +23,9 @@ def settings(request):\n form = EmailForm(data=request.POST)\n if form.is_valid():\n email = form.cleaned_data['email']\n- is_optout = EmailOptout.objects.filter(email=email).count() > 0\n- context = {\n- 'email': email,\n- 'is_optout': is_optout,\n- }\n- return render(request, 'patchwork/mail-settings.html', context)\n+ return HttpResponseRedirect(\n+ reverse('mail-configure', kwargs={'email': email}),\n+ )\n else:\n form = EmailForm()\n \n@@ -35,91 +33,125 @@ def settings(request):\n 'form': form,\n }\n \n- return render(request, 'patchwork/mail.html', context)\n+ return render(request, 'patchwork/mail-settings.html', context)\n \n \n-def optout_confirm(request, conf):\n- email = conf.email.strip().lower()\n- # silently ignore duplicated optouts\n- if EmailOptout.objects.filter(email=email).count() == 0:\n- optout = EmailOptout(email=email)\n- optout.save()\n+def _opt_in(request, email):\n+ EmailConfirmation.objects.filter(type='optin', email=email).delete()\n \n- conf.deactivate()\n+ confirmation = EmailConfirmation(type='optin', email=email)\n+ confirmation.save()\n \n- context = {\n- 'email': conf.email,\n- }\n+ context = {'confirmation': confirmation}\n+ subject = render_to_string('patchwork/mails/optin-request-subject.txt')\n+ message = render_to_string(\n+ 'patchwork/mails/optin-request.txt', context, request=request)\n \n- return render(request, 'patchwork/optout.html', context)\n+ try:\n+ send_mail(subject, message, conf_settings.DEFAULT_FROM_EMAIL, [email])\n+ except smtplib.SMTPException:\n+ messages.error(\n+ request,\n+ 'An error occurred while submitting this request. '\n+ 'Please contact an administrator.'\n+ )\n+ return False\n \n+ messages.success(\n+ request,\n+ 'Requested opt-in to email from Patchwork. '\n+ 'Check your email for confirmation.',\n+ )\n \n-def optin_confirm(request, conf):\n- email = conf.email.strip().lower()\n- EmailOptout.objects.filter(email=email).delete()\n+ return True\n \n- conf.deactivate()\n \n- context = {\n- 'email': conf.email,\n- }\n+def _opt_out(request, email):\n+ EmailConfirmation.objects.filter(type='optout', email=email).delete()\n \n- return render(request, 'patchwork/optin.html', context)\n+ confirmation = EmailConfirmation(type='optout', email=email)\n+ confirmation.save()\n \n+ context = {'confirmation': confirmation}\n+ subject = render_to_string('patchwork/mails/optout-request-subject.txt')\n+ message = render_to_string(\n+ 'patchwork/mails/optout-request.txt', context, request=request)\n \n-def _optinout(request, action):\n- context = {}\n- mail_template = 'patchwork/mails/%s-request.txt' % action\n- mail_subject_template = 'patchwork/mails/%s-request-subject.txt' % action\n- html_template = 'patchwork/%s-request.html' % action\n+ try:\n+ send_mail(subject, message, conf_settings.DEFAULT_FROM_EMAIL, [email])\n+ except smtplib.SMTPException:\n+ messages.error(\n+ request,\n+ 'An error occurred while submitting this request. '\n+ 'Please contact an administrator.'\n+ )\n+ return False\n \n- if request.method != 'POST':\n- return HttpResponseRedirect(reverse(settings))\n+ messages.success(\n+ request,\n+ 'Requested opt-out of email from Patchwork. '\n+ 'Check your email for confirmation.',\n+ )\n+\n+ return True\n \n- form = EmailForm(data=request.POST)\n+\n+def configure(request, email):\n+ # Yes, we're kind of abusing forms here, but this is easier than doing our\n+ # own view-based validation\n+ form = EmailForm(data={'email': email})\n if not form.is_valid():\n- context['error'] = (\n- 'There was an error in the form. Please review ' 'and re-submit.'\n- )\n- context['form'] = form\n- return render(request, html_template, context)\n+ # don't worry - Django escapes these by default\n+ messages.error(request, f'{email} is not a valid email address.')\n+ return HttpResponseRedirect(reverse(settings))\n \n email = form.cleaned_data['email']\n- if (\n- action == 'optin' and\n- EmailOptout.objects.filter(email=email).count() == 0\n- ):\n- context['error'] = (\n- \"The email address %s is not on the patchwork \"\n- \"opt-out list, so you don't need to opt back in\" % email\n- )\n- context['form'] = form\n- return render(request, html_template, context)\n \n- conf = EmailConfirmation(type=action, email=email)\n- conf.save()\n+ if request.method == 'POST':\n+ if 'optin' in request.POST:\n+ if _opt_in(request, email):\n+ return HttpResponseRedirect(reverse('project-list'))\n+ elif 'optout' in request.POST:\n+ if _opt_out(request, email):\n+ return HttpResponseRedirect(reverse('project-list'))\n+ else:\n+ messages.error(request, 'Invalid request.')\n+\n+ is_optout = EmailOptout.objects.filter(email=email).count() > 0\n+ context = {\n+ 'email': email,\n+ 'is_optout': is_optout,\n+ }\n \n- context['confirmation'] = conf\n+ return render(request, 'patchwork/mail-configure.html', context)\n \n- subject = render_to_string(mail_subject_template)\n- message = render_to_string(mail_template, context, request=request)\n \n- try:\n- send_mail(subject, message, conf_settings.DEFAULT_FROM_EMAIL, [email])\n- except smtplib.SMTPException:\n- context['confirmation'] = None\n- context['error'] = (\n- 'An error occurred during confirmation. '\n- 'Please try again later.'\n- )\n- context['admins'] = conf_settings.ADMINS\n+def optout_confirm(request, confirmation):\n+ email = confirmation.email.strip().lower()\n+ # silently ignore duplicated optouts\n+ if EmailOptout.objects.filter(email=email).count() == 0:\n+ optout = EmailOptout(email=email)\n+ optout.save()\n \n- return render(request, html_template, context)\n+ confirmation.deactivate()\n \n+ messages.success(\n+ request,\n+ 'Successfully opted out of email from Patchwork.'\n+ )\n+\n+ return HttpResponseRedirect(reverse('project-list'))\n+\n+\n+def optin_confirm(request, confirmation):\n+ email = confirmation.email.strip().lower()\n+ EmailOptout.objects.filter(email=email).delete()\n \n-def optout(request):\n- return _optinout(request, 'optout')\n+ confirmation.deactivate()\n \n+ messages.success(\n+ request,\n+ 'Successfully opted into email from Patchwork.'\n+ )\n \n-def optin(request):\n- return _optinout(request, 'optin')\n+ return HttpResponseRedirect(reverse('project-list'))\ndiff --git patchwork/views/notification.py patchwork/views/notification.py\nindex 4e023867..7c773ba7 100644\n--- patchwork/views/notification.py\n+++ patchwork/views/notification.py\n@@ -4,9 +4,9 @@\n #\n # SPDX-License-Identifier: GPL-2.0-or-later\n \n-from django.http import Http404\n-from django.shortcuts import get_object_or_404\n-from django.shortcuts import render\n+from django.contrib import messages\n+from django.http import HttpResponseRedirect\n+from django.urls import reverse\n \n from patchwork.models import EmailConfirmation\n from patchwork.views import mail\n@@ -22,18 +22,27 @@ def confirm(request, key):\n 'optin': mail.optin_confirm,\n }\n \n- conf = get_object_or_404(EmailConfirmation, key=key)\n+ try:\n+ conf = EmailConfirmation.objects.get(key=key)\n+ except EmailConfirmation.DoesNotExist:\n+ messages.error(\n+ request,\n+ 'That request is invalid or expired. Please try again.'\n+ )\n+ return HttpResponseRedirect(reverse('project-list'))\n+\n if conf.type not in views:\n- raise Http404\n+ messages.error(\n+ request,\n+ 'That request is invalid or expired. Please try again.'\n+ )\n+ return HttpResponseRedirect(reverse('project-list'))\n \n if conf.active and conf.is_valid():\n return views[conf.type](request, conf)\n \n- context = {}\n- context['conf'] = conf\n- if not conf.active:\n- context['error'] = 'inactive'\n- elif not conf.is_valid():\n- context['error'] = 'expired'\n-\n- return render(request, 'patchwork/confirm-error.html', context)\n+ messages.error(\n+ request,\n+ 'That request is invalid or expired. Please try again.'\n+ )\n+ return HttpResponseRedirect(reverse('project-list'))\ndiff --git patchwork/views/user.py patchwork/views/user.py\nindex 440aa38a..973061b7 100644\n--- patchwork/views/user.py\n+++ patchwork/views/user.py\n@@ -54,12 +54,12 @@ def register(request):\n user.save()\n \n # create confirmation\n- conf = EmailConfirmation(\n+ confirmation = EmailConfirmation(\n type='registration', user=user, email=user.email\n )\n- conf.save()\n+ confirmation.save()\n \n- context['confirmation'] = conf\n+ context['confirmation'] = confirmation\n \n # send email\n subject = render_to_string(\n@@ -67,12 +67,18 @@ def register(request):\n )\n message = render_to_string(\n 'patchwork/mails/activation.txt',\n- {'site': Site.objects.get_current(), 'confirmation': conf},\n+ {\n+ 'site': Site.objects.get_current(),\n+ 'confirmation': confirmation,\n+ },\n )\n \n try:\n send_mail(\n- subject, message, settings.DEFAULT_FROM_EMAIL, [conf.email]\n+ subject,\n+ message,\n+ settings.DEFAULT_FROM_EMAIL,\n+ [confirmation.email]\n )\n except smtplib.SMTPException:\n context['confirmation'] = None\n@@ -88,26 +94,34 @@ def register(request):\n return render(request, 'patchwork/registration.html', context)\n \n \n-def register_confirm(request, conf):\n- conf.user.is_active = True\n- conf.user.save()\n- conf.deactivate()\n+def register_confirm(request, confirmation):\n+ confirmation.user.is_active = True\n+ confirmation.user.save()\n+ confirmation.deactivate()\n \n try:\n- person = Person.objects.get(email__iexact=conf.user.email)\n+ person = Person.objects.get(email__iexact=confirmation.user.email)\n except Person.DoesNotExist:\n- person = Person(email=conf.user.email, name=conf.user.profile.name)\n- person.user = conf.user\n+ person = Person(\n+ email=confirmation.user.email, name=confirmation.user.profile.name,\n+ )\n+ person.user = confirmation.user\n person.save()\n \n- return render(request, 'patchwork/registration-confirm.html')\n+ messages.success(request, 'Successfully confirmed account.')\n+\n+ return HttpResponseRedirect(reverse('project-list'))\n \n \n def _send_confirmation_email(request, email):\n- conf = EmailConfirmation(type='userperson', user=request.user, email=email)\n- conf.save()\n+ confirmation = EmailConfirmation(\n+ type='userperson',\n+ user=request.user,\n+ email=email,\n+ )\n+ confirmation.save()\n \n- context = {'confirmation': conf}\n+ context = {'confirmation': confirmation}\n subject = render_to_string('patchwork/mails/user-link-subject.txt')\n message = render_to_string(\n 'patchwork/mails/user-link.txt',\n@@ -273,15 +287,15 @@ def profile(request):\n \n \n @login_required\n-def link_confirm(request, conf):\n+def link_confirm(request, confirmation):\n try:\n- person = Person.objects.get(email__iexact=conf.email)\n+ person = Person.objects.get(email__iexact=confirmation.email)\n except Person.DoesNotExist:\n- person = Person(email=conf.email)\n+ person = Person(email=confirmation.email)\n \n- person.link_to_user(conf.user)\n+ person.link_to_user(confirmation.user)\n person.save()\n- conf.deactivate()\n+ confirmation.deactivate()\n \n messages.success(request, 'Successfully linked email to account.')\n \n", "prefixes": [ "RFC", "v2", "18/19" ] }