Commit 858a4b32 authored by stephen's avatar stephen
Browse files

Merge branch 'main' into improve-newsletter-captcha-error-display

parents e8f1d387 840667ab
Loading
Loading
Loading
Loading
+20 −56
Original line number Diff line number Diff line
# This file is automatically @generated by Poetry and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.

[[package]]
name = "asgiref"
version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -19,7 +18,6 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
name = "async-stripe"
version = "5.5.0"
description = "An asynchronous wrapper around Stripe's official python library."
category = "main"
optional = false
python-versions = ">=3.6,<4.0"
files = [
@@ -35,7 +33,6 @@ tornado = ">=5.1"
name = "async-timeout"
version = "4.0.3"
description = "Timeout context manager for asyncio programs"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -47,7 +44,6 @@ files = [
name = "beautifulsoup4"
version = "4.12.3"
description = "Screen-scraping library"
category = "main"
optional = false
python-versions = ">=3.6.0"
files = [
@@ -69,7 +65,6 @@ lxml = ["lxml"]
name = "certifi"
version = "2024.7.4"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -81,7 +76,6 @@ files = [
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
@@ -181,7 +175,6 @@ files = [
name = "coverage"
version = "7.6.1"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -266,7 +259,6 @@ toml = ["tomli"]
name = "dependency-injector"
version = "4.41.0"
description = "Dependency injection framework for Python"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -355,7 +347,6 @@ yaml = ["pyyaml"]
name = "django"
version = "4.2.15"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -376,7 +367,6 @@ bcrypt = ["bcrypt"]
name = "django-bootstrap-v5"
version = "1.0.11"
description = "Bootstrap 5 support for Django projects"
category = "main"
optional = false
python-versions = ">=3.6,<4.0"
files = [
@@ -395,7 +385,6 @@ docs = ["m2r2 (>=0.2.5,<0.3.0)", "sphinx (>=4.4,<5.0)", "sphinx_rtd_theme (>=1.0
name = "django-jinja"
version = "2.11.0"
description = "Jinja2 templating language integrated in Django."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -411,7 +400,6 @@ jinja2 = ">=3"
name = "django-jinja-bootstrap-form"
version = "4.5.0"
description = "django-jinja-bootstrap-form"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -427,7 +415,6 @@ django-jinja = ">=2.6.0"
name = "django-prometheus"
version = "2.3.1"
description = "Django middlewares to monitor your application with Prometheus.io."
category = "main"
optional = false
python-versions = "*"
files = [
@@ -442,7 +429,6 @@ prometheus-client = ">=0.7"
name = "django-ranged-response"
version = "0.2.0"
description = "Modified Django FileResponse that adds Content-Range headers."
category = "main"
optional = false
python-versions = "*"
files = [
@@ -456,7 +442,6 @@ django = "*"
name = "django-ratelimit"
version = "4.1.0"
description = "Cache-based rate-limiting for Django."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -468,7 +453,6 @@ files = [
name = "django-simple-captcha"
version = "0.6.1"
description = "A very simple, yet powerful, Django captcha application"
category = "main"
optional = false
python-versions = "*"
files = []
@@ -477,6 +461,7 @@ develop = false
[package.dependencies]
Django = ">=4.2"
django-ranged-response = "0.2.0"
djangorestframework = ">=3.15.0"
Pillow = ">=6.2.0"

[package.extras]
@@ -485,14 +470,27 @@ test = ["testfixtures"]
[package.source]
type = "git"
url = "https://github.com/mbi/django-simple-captcha.git"
reference = "bf7fffc01b989ab411bbc29d7b27c465a0ba5289"
resolved_reference = "bf7fffc01b989ab411bbc29d7b27c465a0ba5289"
reference = "1c92409dd05475a94657711a048732228bf6288a"
resolved_reference = "1c92409dd05475a94657711a048732228bf6288a"

[[package]]
name = "djangorestframework"
version = "3.15.2"
description = "Web APIs for Django, made easy."
optional = false
python-versions = ">=3.8"
files = [
    {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"},
    {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"},
]

[package.dependencies]
django = ">=4.2"

[[package]]
name = "dnspython"
version = "2.6.1"
description = "DNS toolkit"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -513,7 +511,6 @@ wmi = ["wmi (>=1.5.1)"]
name = "email-validator"
version = "2.2.0"
description = "A robust email address syntax and deliverability validation library."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -529,7 +526,6 @@ idna = ">=2.0.0"
name = "fakeredis"
version = "2.24.1"
description = "Python implementation of redis API, can be used for testing purposes."
category = "dev"
optional = false
python-versions = "<4.0,>=3.7"
files = [
@@ -552,7 +548,6 @@ probabilistic = ["pyprobables (>=0.6,<0.7)"]
name = "flake8"
version = "6.1.0"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.8.1"
files = [
@@ -569,7 +564,6 @@ pyflakes = ">=3.1.0,<3.2.0"
name = "flake8-isort"
version = "6.1.1"
description = "flake8 plugin that integrates isort"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -588,7 +582,6 @@ test = ["pytest"]
name = "flake8-pyproject"
version = "1.2.3"
description = "Flake8 plug-in loading the configuration from pyproject.toml"
category = "dev"
optional = false
python-versions = ">= 3.6"
files = [
@@ -605,7 +598,6 @@ dev = ["pyTest", "pyTest-cov"]
name = "gunicorn"
version = "20.1.0"
description = "WSGI HTTP Server for UNIX"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -626,7 +618,6 @@ tornado = ["tornado (>=0.2)"]
name = "idna"
version = "3.8"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -638,7 +629,6 @@ files = [
name = "isort"
version = "5.13.2"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.8.0"
files = [
@@ -653,7 +643,6 @@ colors = ["colorama (>=0.4.6)"]
name = "jinja2"
version = "3.1.4"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -671,7 +660,6 @@ i18n = ["Babel (>=2.7)"]
name = "markupsafe"
version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -741,7 +729,6 @@ files = [
name = "mccabe"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@@ -753,7 +740,6 @@ files = [
name = "mypy"
version = "1.11.2"
description = "Optional static typing for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -800,7 +786,6 @@ reports = ["lxml"]
name = "mypy-extensions"
version = "1.0.0"
description = "Type system extensions for programs checked with the mypy type checker."
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -812,7 +797,6 @@ files = [
name = "pillow"
version = "10.4.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -910,7 +894,6 @@ xmp = ["defusedxml"]
name = "prometheus-client"
version = "0.20.0"
description = "Python client for the Prometheus monitoring system."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -925,7 +908,6 @@ twisted = ["twisted"]
name = "pycodestyle"
version = "2.11.1"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -937,7 +919,6 @@ files = [
name = "pyflakes"
version = "3.1.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -949,7 +930,6 @@ files = [
name = "python-dotenv"
version = "1.0.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -964,7 +944,6 @@ cli = ["click (>=5.0)"]
name = "redis"
version = "5.0.8"
description = "Python client for Redis database and key-value store"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -983,7 +962,6 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1005,7 +983,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
name = "requests-mock"
version = "1.12.1"
description = "Mock out responses from the requests package"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -1023,7 +1000,6 @@ fixture = ["fixtures"]
name = "setuptools"
version = "74.0.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -1038,13 +1014,12 @@ cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.11.0,<1.12.0)", "pytest-mypy"]
type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"]

[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
@@ -1056,7 +1031,6 @@ files = [
name = "sortedcontainers"
version = "2.4.0"
description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
category = "dev"
optional = false
python-versions = "*"
files = [
@@ -1068,7 +1042,6 @@ files = [
name = "soupsieve"
version = "2.6"
description = "A modern CSS selector implementation for Beautiful Soup."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1080,7 +1053,6 @@ files = [
name = "sqlparse"
version = "0.5.1"
description = "A non-validating SQL parser."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1096,7 +1068,6 @@ doc = ["sphinx"]
name = "stripe"
version = "5.5.0"
description = "Python bindings for the Stripe API"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@@ -1111,7 +1082,6 @@ requests = {version = ">=2.20", markers = "python_version >= \"3.0\""}
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@@ -1123,7 +1093,6 @@ files = [
name = "tornado"
version = "6.4.1"
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1144,7 +1113,6 @@ files = [
name = "types-requests"
version = "2.32.0.20240712"
description = "Typing stubs for requests"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1159,7 +1127,6 @@ urllib3 = ">=2"
name = "types-stripe"
version = "3.5.2.20240106"
description = "Typing stubs for stripe"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1171,7 +1138,6 @@ files = [
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@@ -1183,7 +1149,6 @@ files = [
name = "tzdata"
version = "2024.1"
description = "Provider of IANA time zone data"
category = "main"
optional = false
python-versions = ">=2"
files = [
@@ -1195,7 +1160,6 @@ files = [
name = "urllib3"
version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1212,4 +1176,4 @@ zstd = ["zstandard (>=0.18.0)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "aa40551da0d21287e2177518c6f023f2e62ee6ddf2c75df8c25addf5760d51de"
content-hash = "da05afb7869c76f57a76c97e157e76b748605e6854067eee7bbf5888932f56fb"
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
Django = "^4.2.4"
django-simple-captcha = { git = "https://github.com/mbi/django-simple-captcha.git", rev = "bf7fffc01b989ab411bbc29d7b27c465a0ba5289" }
django-simple-captcha = { git = "https://github.com/mbi/django-simple-captcha.git", rev = "1c92409dd05475a94657711a048732228bf6288a" }
django-bootstrap-v5 = "^1.0.11"
django-jinja = "^2.10.2"
django-jinja-bootstrap-form = "^4.5.0"
+3 −3
Original line number Diff line number Diff line
@@ -61,12 +61,12 @@ dTor.init = function () {
    $("form.was-validated").removeClass("was-validated");
    dTor.refreshCaptcha();
    const messageContainer = $("#result-message");
    messageContainer.html(message).removeClass("hidden");
    messageContainer.text(message).removeClass("hidden");
  };

  this.hidePaymentError = function() {
    const messageContainer = $("#result-message");
    messageContainer.html("").addClass("hidden");
    messageContainer.text("").addClass("hidden");
  }

  this.handleFormErrors = function (errors) {
@@ -118,7 +118,7 @@ dTor.init = function () {
  
  this.validateElement = function (el) {
    if (!el.checkValidity() || $(el).val() == null) {
      $(el).siblings(".invalid-feedback").html(el.validationMessage);
      $(el).siblings(".invalid-feedback").text(el.validationMessage);
      $(el).addClass("is-invalid");
    } else {
      $(el).removeClass("is-invalid");
+18 −7
Original line number Diff line number Diff line
@@ -331,8 +331,10 @@ If this error persists, please reach out to us via one of the methods
    # IPN timestamp info: https://developer.paypal.com/api/nvp-soap/ipn/IPNandPDTVariables/#payment-information  # noqa: E501
    # Relevant Python Github issue regarding strptime's %Z matching: https://github.com/python/cpython/issues/66571  # noqa: E501
    def ipn_time_to_datetime(self, ipn_time: str) -> datetime:
        # Replace timestamp whether or not it's daylight savings time in the US
        ipn_time = ipn_time.replace("PDT", "-0700").replace("PST", "-0800")
        return datetime.strptime(
            ipn_time.replace("PDT", "-0700"),
            ipn_time,
            "%H:%M:%S %b %d, %Y %z"
        ).astimezone(
            timezone.utc
@@ -353,7 +355,7 @@ If this error persists, please reach out to us via one of the methods
        # that we do from webhooks, so we pick and choose the particular information from it
        # which will allow us to hand this info off to the rest of the donation-processing
        # pipeline.
        # (IPN messages always, and only, relate to ongoing subscriptions, so we can
        # (IPN messages we care about always, and only, relate to ongoing subscriptions, so we can
        # hardcode the message and make some other assumptions about what's in `args`.)
        message = "Tor\\Donation\\RecurringContributionOngoing"

@@ -386,8 +388,8 @@ If this error persists, please reach out to us via one of the methods
        args = {
            "payment_instrument_id": "PayPal",
            "receive_date": timestamp,
            "trxn_id": param_dict["txn_id"],
            "handshake_id": param_dict["txn_id"],
            "trxn_id": param_dict["recurring_payment_id"],
            "handshake_id": param_dict["recurring_payment_id"],
            "recurring_contribution_transaction_id": param_dict["recurring_payment_id"],
            "currency": param_dict["mc_currency"],
            "total_amount": param_dict["amount"],
@@ -404,8 +406,17 @@ If this error persists, please reach out to us via one of the methods
            billing_result, "recurring", "paypal"
        ).inc()

        # Ensure that IPN messages are deduped before being handed off for processing.
        if await self._civi.donation_exists(message, args["trxn_id"]) is False:
        # Ensure that donations are deduped before being handed off for processing.
        # In the event that PayPal sends both an IPN message and a webhook message about the
        # same legacy transaction, we need to use a deduping value available to both message
        # types; since `txn_id` is an IPN-exclusive value, we instead use `recurring_payment_id`.
        # Note that this ID is unique to this transaction series, and not this individual
        # transaction; since recurring transactions only bill once a month, and since we only
        # check for duplicate transaction messages for an hour after the first message, this
        # value is both unique enough and broadly-available enough for our purposes.
        if await self._civi.donation_exists(
            message, args["trxn_id"]
        ) is False:
            try:
                await self._civi.report_donation(message, args)
            except ValueError:
@@ -515,7 +526,7 @@ If this error persists, please reach out to us via one of the methods
            # A subscription's donation has succeeded
            case "PAYMENT.SALE.COMPLETED":
                message = "Tor\\Donation\\RecurringContributionOngoing"
                args["trxn_id"] = event["id"]
                args["trxn_id"] = event["resource"]["id"]
                # In the case of new Paypal subscriptions, the only ID string found both in the
                # post-donation success data _and_ the webhook confirming the payment went through
                # is the billing agreement ID. This works fine in practice, but it is the one-off
+1 −1

File changed.

Preview size limit exceeded, changes collapsed.

Loading