---
siteapps/sightings/views.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/siteapps/sightings/views.py b/siteapps/sightings/views.py
index 6b64dd3..f6f034b 100644
--- a/siteapps/sightings/views.py
+++ b/siteapps/sightings/views.py
@@ -139,6 +139,8 @@ def post(self, request):
data["obfuscationKilometers"] = obfuscation_km
elif data.get("privacySetting") == "obscured":
data["obfuscationKilometers"] = 2 # Default 2km when obscured and not explicitly set
+ if request.POST.get("device_type"):
+ data["deviceType"] = request.POST.get("device_type")
if request.POST.get("camera_model"):
data["cameraModel"] = request.POST.get("camera_model")
if request.POST.get("camera_deployment_date"):
From 2c84a68f9416835a3c756bd7d0851ad80a95faa4 Mon Sep 17 00:00:00 2001
From: irenecancode <196759302+irenecancode@users.noreply.github.com>
Date: Tue, 28 Jul 2026 12:42:43 -0700
Subject: [PATCH 4/4] Update privacy policy checkbox for submission form
---
siteapps/sightings/tests.py | 43 ++++
siteapps/sightings/views.py | 6 +
.../components/privacy_policy_content.html | 207 ++++++++++++++++++
.../templates/sightings/create_sighting.html | 98 ++++++++-
4 files changed, 353 insertions(+), 1 deletion(-)
create mode 100644 siteapps/templates/components/privacy_policy_content.html
diff --git a/siteapps/sightings/tests.py b/siteapps/sightings/tests.py
index ab1f627..917dc31 100644
--- a/siteapps/sightings/tests.py
+++ b/siteapps/sightings/tests.py
@@ -93,6 +93,7 @@ def test_post_missing_title_shows_error(self, mock_geocode, mock_client_class):
"encounter_date": "2024-01-01",
"location_latitude": "45.0",
"location_longitude": "-93.0",
+ "privacy_accepted": "1",
},
)
msgs = [str(m) for m in get_messages(response.wsgi_request)]
@@ -113,6 +114,7 @@ def test_post_missing_datetime_shows_error(self, mock_geocode, mock_client_class
"post_title": "Bird sighting",
"location_latitude": "45.0",
"location_longitude": "-93.0",
+ "privacy_accepted": "1",
},
)
msgs = [str(m) for m in get_messages(response.wsgi_request)]
@@ -132,6 +134,7 @@ def test_post_missing_location_shows_error(self, mock_geocode, mock_client_class
{
"post_title": "Bird",
"encounter_date": "2024-01-01",
+ "privacy_accepted": "1",
},
)
msgs = [str(m) for m in get_messages(response.wsgi_request)]
@@ -163,6 +166,7 @@ def test_successful_submission_redirects_to_feed(self, mock_geocode, mock_client
"privacy_setting": "public",
"location_accuracy_meters": "5",
"species_list": ["Robin"],
+ "privacy_accepted": "1",
},
)
self.assertRedirects(response, reverse("socialmedia:feed"), fetch_redirect_response=False)
@@ -184,6 +188,7 @@ def test_api_submission_failure_shows_error(self, mock_geocode, mock_client_clas
"encounter_date": "2024-01-01",
"location_latitude": "45.5",
"location_longitude": "-122.7",
+ "privacy_accepted": "1",
},
)
msgs = [str(m) for m in get_messages(response.wsgi_request)]
@@ -205,6 +210,7 @@ def test_invalid_coordinates_shows_error(self, mock_geocode, mock_client_class):
"encounter_date": "2024-01-01",
"location_latitude": "not_a_number",
"location_longitude": "-93.0",
+ "privacy_accepted": "1",
},
)
msgs = [str(m) for m in get_messages(response.wsgi_request)]
@@ -212,6 +218,43 @@ def test_invalid_coordinates_shows_error(self, mock_geocode, mock_client_class):
any("latitude" in m.lower() or "invalid" in m.lower() or "longitude" in m.lower() for m in msgs)
)
+ @patch("siteapps.sightings.views.BackendAPIClient")
+ @patch("siteapps.sightings.views.reverse_geocode_with_nominatim")
+ def test_post_without_privacy_consent_is_rejected(self, mock_geocode, mock_client_class):
+ self._login_with_token()
+ mock_geocode.return_value = None
+ mock_api = MagicMock()
+ mock_api.get.return_value = {"species_names": []}
+ mock_client_class.return_value = mock_api
+
+ response = self.client.post(
+ self.url,
+ {
+ "post_title": "Bird sighting",
+ "encounter_date": "2024-01-01",
+ "encounter_time": "10:00",
+ "location_latitude": "45.5",
+ "location_longitude": "-122.7",
+ "privacy_setting": "public",
+ },
+ )
+
+ self.assertEqual(response.status_code, 200)
+ mock_api.post.assert_not_called()
+ msgs = [str(m) for m in get_messages(response.wsgi_request)]
+ self.assertTrue(any("privacy policy" in m.lower() for m in msgs))
+
+ def test_form_renders_privacy_consent_checkbox(self):
+ self._login_with_token()
+ response = self.client.get(self.url)
+ self.assertContains(response, 'name="privacy_accepted"')
+ self.assertContains(response, "privacyPolicyModal")
+ # The policy text itself is inlined in the modal, not just linked.
+ self.assertContains(response, "1. Information We Collect")
+ self.assertContains(response, "Last Updated:")
+ # A template placeholder must never reach a consent-gated document.
+ self.assertNotContains(response, "[Insert Contact Email]")
+
class MySightingsViewTests(TestCase):
def setUp(self):
diff --git a/siteapps/sightings/views.py b/siteapps/sightings/views.py
index f6f034b..b43dabd 100644
--- a/siteapps/sightings/views.py
+++ b/siteapps/sightings/views.py
@@ -67,6 +67,12 @@ def post(self, request):
messages.error(request, "Authentication required.")
return redirect("users:login")
+ # Consent is enforced here as well as in the form, so a direct POST
+ # cannot skip it.
+ if not request.POST.get("privacy_accepted"):
+ messages.error(request, "You must accept the Privacy Policy before publishing a sighting.")
+ return self.get(request)
+
# Extract form data and transform to camelCase format for backend API
encounter_date = request.POST.get("encounter_date")
encounter_time = request.POST.get("encounter_time", "12:00")
diff --git a/siteapps/templates/components/privacy_policy_content.html b/siteapps/templates/components/privacy_policy_content.html
new file mode 100644
index 0000000..e3373bb
--- /dev/null
+++ b/siteapps/templates/components/privacy_policy_content.html
@@ -0,0 +1,207 @@
+{% comment %}
+WildeBackyard privacy policy body — text only, no page chrome and no colour
+overrides, so it renders correctly inside a modal or a standalone page.
+
+Source: "Privacy Policy for WildeBackyard", effective 06/29/2026, last updated
+07/27/2026. Note that wildepod.org (backyard/privacy.html) still serves the
+superseded 07/04/2024 policy and needs the same update.
+{% endcomment %}
+
+ Effective Date: 06/29/2026
+
+ Last Updated: 07/27/2026
+
+
+ At WildeBackyard (“we,” “us,” or “our”), a community science and social sharing initiative operated
+ in association with the Felidae Conservation Fund, we are committed to respecting your privacy while advancing
+ wildlife research and conservation.
+
+
+ This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you use the
+ WildeBackyard website, mobile applications, and associated services (collectively, the “Platform”).
+
+
+ Please read this Privacy Policy carefully. By accessing or using the Platform, you acknowledge that you have read,
+ understood, and agree to the practices described in this policy.
+
+
+1. Information We Collect
+
+ We collect information directly from you, automatically through your use of the Platform, and from third-party
+ sources.
+
+A. Information You Provide Directly
+
+ -
+ Account Information: Name, username, email address, password, profile picture, and optional bio
+ when you register for an account.
+
+ -
+ Community Science & Social Uploads: Wildlife photos, video recordings, audio files,
+ observation notes, species tags, comments, likes, and community forum posts you contribute to the Platform.
+
+ -
+ Donations & Financial Transactions: If you make a donation or purchase through the Platform,
+ we collect billing details, donation amounts, and contact information. Payment processing details (such as
+ credit card numbers) are handled directly by secure third-party payment processors and are not stored on our
+ servers.
+
+ -
+ Communications: Information you provide when contacting us for support, participating in
+ surveys, or subscribing to updates.
+
+
+B. Location & Spatial Data (Community Science Observations)
+Because WildeBackyard is a community science platform dedicated to ecological and wildlife research:
+
+ -
+ Geolocation & EXIF Metadata: When you upload wildlife photos or log sightings, we collect
+ precise or approximate geographic coordinates (latitude and longitude), elevation, date, and timestamp from your
+ device or media files.
+
+ -
+ Privacy Controls for Sensitive Locations: You may have the option to obscure or "blur" precise
+ observation locations (e.g., sightings on private property or involving sensitive/threatened species) according
+ to your platform preferences.
+
+
+C. Information Collected Automatically
+
+ -
+ Device & Usage Data: IP address, browser type, operating system, unique device identifiers,
+ pages viewed, time spent on pages, and navigation paths.
+
+ -
+ Cookies and Tracking Technologies: We use cookies, pixels, and similar tools to maintain session
+ state, remember user preferences, and analyze platform usage.
+
+
+2. How We Use Your Information
+We use the information we collect for the following purposes:
+
+ -
+ Platform Operation & Social Sharing: To create and manage your account, display your public
+ profile and shared observations, facilitate social interaction (comments, likes, shares), and deliver core
+ features.
+
+ -
+ Scientific Research & Wildlife Conservation: To aggregate, analyze, and map wildlife
+ sighting data. This data helps researchers, conservation scientists, and ecological partners monitor
+ biodiversity, study wildlife corridors, and advance non-profit conservation efforts.
+
+ -
+ Community Engagement: To notify you about platform activity, updates on research projects you
+ contribute to, community challenges, and conservation news.
+
+ -
+ Processing Donations: To process contributions, issue tax receipts, and communicate regarding
+ fundraising initiatives.
+
+ -
+ Platform Security & Improvement: To maintain network security, troubleshoot technical
+ issues, prevent fraudulent activity, and enhance user experience.
+
+ -
+ Legal Compliance: To fulfill legal obligations and enforce our Terms of Service.
+
+
+3. How We Share Your Information
+
+ We do not sell your personal identification information to third parties. We share information only in the following
+ contexts:
+
+A. Public & Community Sharing
+
+ -
+ Public Sighting Data: Observations, species tags, non-obscured location data, photos, usernames,
+ and profile details you choose to share publicly on the Platform will be visible to other users and visitors.
+
+ -
+ Obscured / Private Observations: If you mark a location as private or if a species is flagged as
+ sensitive, location data displayed publicly may be generalized or obscured.
+
+
+B. Scientific & Conservation Research Partners
+
+ -
+ Community science data (including observation locations, timestamps, and media) may be shared with scientific
+ partners, universities, wildlife agencies, and affiliated conservation organizations (including the Felidae
+ Conservation Fund) for non-commercial research, environmental modeling, and habitat protection.
+
+
+C. Service Providers
+
+ -
+ We share data with trusted third-party vendors who assist us in operating the Platform (e.g., cloud hosting, data
+ analytics, email delivery, customer support, and payment gateways). These providers are bound by strict
+ confidentiality obligations.
+
+
+D. Legal & Safety Requirements
+
+ -
+ We may disclose information if required by law, court order, or government regulation, or if we believe in good
+ faith that disclosure is necessary to protect the rights, property, or safety of WildeBackyard, our users,
+ wildlife, or the public.
+
+
+4. Your Choices & Data Rights
+
+ -
+ Account & Profile Settings: You can review, update, or edit your account information and
+ profile visibility settings at any time through your account settings.
+
+ -
+ Location Controls: You can control location permissions via your device settings or choose to
+ blur/obscure specific observation locations prior to publishing.
+
+ -
+ Email Communications: You can opt out of promotional emails or newsletters by clicking the
+ "Unsubscribe" link in any promotional message. Essential transactional and account updates will still be sent.
+
+ -
+ Data Deletion: You may request the deletion of your account and personal data by contacting us
+ at privacy@felidaefund.org. Please note that anonymized or
+ aggregated community science research data previously contributed to research databases may be retained for
+ scientific integrity.
+
+
+5. Data Security
+
+ We implement appropriate technical and organizational security measures to safeguard your personal information
+ against unauthorized access, loss, alteration, or misuse. However, no internet transmission or electronic storage
+ method is 100% secure, and we cannot guarantee absolute security.
+
+6. Children’s Privacy
+
+ WildeBackyard is not intended for children under the age of 13 (or 16 in certain jurisdictions) without parental
+ consent. We do not knowingly collect personal information directly from children under these ages. If you believe a
+ child has provided us with personal information without parental consent, please contact us so we can take
+ appropriate steps to remove the information.
+
+7. Third-Party Links & Services
+
+ The Platform may contain links to third-party websites or services not operated by WildeBackyard (including partner
+ conservation sites). We are not responsible for the privacy practices or content of third-party websites. We
+ encourage you to review the privacy policies of any site you visit.
+
+8. Updates to This Privacy Policy
+
+ We may update this Privacy Policy from time to time to reflect changes in our practices, platform features, or legal
+ requirements. When we post updates, we will revise the "Last Updated" date at the top of this page. For material
+ changes, we will notify you through the Platform or via email.
+
+9. Contact Us
+
+ If you have any questions, concerns, or requests regarding this Privacy Policy or our data practices, please contact
+ us at:
+
+
+ WildeBackyard / Felidae Conservation Fund
+
+ Email: privacy@felidaefund.org / support@felidaefund.org
+
+ Mailing Address: 655 Redwood Hwy, Suite 150, Mill Valley, CA 94941
+
+ Website: https://felidaefund.org / https://wildebackyard.com
+
diff --git a/siteapps/templates/sightings/create_sighting.html b/siteapps/templates/sightings/create_sighting.html
index 1bc4b5f..de8d8be 100644
--- a/siteapps/templates/sightings/create_sighting.html
+++ b/siteapps/templates/sightings/create_sighting.html
@@ -700,6 +700,21 @@