Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions siteapps/sightings/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,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)]
Expand All @@ -116,6 +117,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)]
Expand All @@ -135,6 +137,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)]
Expand Down Expand Up @@ -166,6 +169,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)
Expand All @@ -187,6 +191,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)]
Expand All @@ -208,13 +213,51 @@ 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)]
self.assertTrue(
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):
Expand Down
8 changes: 8 additions & 0 deletions siteapps/sightings/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,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")
Expand Down Expand Up @@ -142,6 +148,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"):
Expand Down
207 changes: 207 additions & 0 deletions siteapps/templates/components/privacy_policy_content.html
Original file line number Diff line number Diff line change
@@ -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 %}
<p class="text-muted small mb-3">
<strong>Effective Date:</strong> 06/29/2026
<br>
<strong>Last Updated:</strong> 07/27/2026
</p>
<p>
At <strong>WildeBackyard</strong> (“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.
</p>
<p>
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”).
</p>
<p>
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.
</p>
<hr>
<h4 class="mt-4">1. Information We Collect</h4>
<p>
We collect information directly from you, automatically through your use of the Platform, and from third-party
sources.
</p>
<h5 class="mt-4">A. Information You Provide Directly</h5>
<ul>
<li>
<strong>Account Information:</strong> Name, username, email address, password, profile picture, and optional bio
when you register for an account.
</li>
<li>
<strong>Community Science &amp; Social Uploads:</strong> Wildlife photos, video recordings, audio files,
observation notes, species tags, comments, likes, and community forum posts you contribute to the Platform.
</li>
<li>
<strong>Donations &amp; Financial Transactions:</strong> If you make a donation or purchase through the Platform,
we collect billing details, donation amounts, and contact information. <em>Payment processing details (such as
credit card numbers) are handled directly by secure third-party payment processors and are not stored on our
servers.</em>
</li>
<li>
<strong>Communications:</strong> Information you provide when contacting us for support, participating in
surveys, or subscribing to updates.
</li>
</ul>
<h5 class="mt-4">B. Location &amp; Spatial Data (Community Science Observations)</h5>
<p>Because WildeBackyard is a community science platform dedicated to ecological and wildlife research:</p>
<ul>
<li>
<strong>Geolocation &amp; EXIF Metadata:</strong> 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.
</li>
<li>
<strong>Privacy Controls for Sensitive Locations:</strong> 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.
</li>
</ul>
<h5 class="mt-4">C. Information Collected Automatically</h5>
<ul>
<li>
<strong>Device &amp; Usage Data:</strong> IP address, browser type, operating system, unique device identifiers,
pages viewed, time spent on pages, and navigation paths.
</li>
<li>
<strong>Cookies and Tracking Technologies:</strong> We use cookies, pixels, and similar tools to maintain session
state, remember user preferences, and analyze platform usage.
</li>
</ul>
<h4 class="mt-4">2. How We Use Your Information</h4>
<p>We use the information we collect for the following purposes:</p>
<ul>
<li>
<strong>Platform Operation &amp; Social Sharing:</strong> To create and manage your account, display your public
profile and shared observations, facilitate social interaction (comments, likes, shares), and deliver core
features.
</li>
<li>
<strong>Scientific Research &amp; Wildlife Conservation:</strong> 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.
</li>
<li>
<strong>Community Engagement:</strong> To notify you about platform activity, updates on research projects you
contribute to, community challenges, and conservation news.
</li>
<li>
<strong>Processing Donations:</strong> To process contributions, issue tax receipts, and communicate regarding
fundraising initiatives.
</li>
<li>
<strong>Platform Security &amp; Improvement:</strong> To maintain network security, troubleshoot technical
issues, prevent fraudulent activity, and enhance user experience.
</li>
<li>
<strong>Legal Compliance:</strong> To fulfill legal obligations and enforce our Terms of Service.
</li>
</ul>
<h4 class="mt-4">3. How We Share Your Information</h4>
<p>
We do not sell your personal identification information to third parties. We share information only in the following
contexts:
</p>
<h5 class="mt-4">A. Public &amp; Community Sharing</h5>
<ul>
<li>
<strong>Public Sighting Data:</strong> 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.
</li>
<li>
<strong>Obscured / Private Observations:</strong> If you mark a location as private or if a species is flagged as
sensitive, location data displayed publicly may be generalized or obscured.
</li>
</ul>
<h5 class="mt-4">B. Scientific &amp; Conservation Research Partners</h5>
<ul>
<li>
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.
</li>
</ul>
<h5 class="mt-4">C. Service Providers</h5>
<ul>
<li>
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.
</li>
</ul>
<h5 class="mt-4">D. Legal &amp; Safety Requirements</h5>
<ul>
<li>
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.
</li>
</ul>
<h4 class="mt-4">4. Your Choices &amp; Data Rights</h4>
<ul>
<li>
<strong>Account &amp; Profile Settings:</strong> You can review, update, or edit your account information and
profile visibility settings at any time through your account settings.
</li>
<li>
<strong>Location Controls:</strong> You can control location permissions via your device settings or choose to
blur/obscure specific observation locations prior to publishing.
</li>
<li>
<strong>Email Communications:</strong> 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.
</li>
<li>
<strong>Data Deletion:</strong> You may request the deletion of your account and personal data by contacting us
at <a href="mailto:privacy@felidaefund.org">privacy@felidaefund.org</a>. Please note that anonymized or
aggregated community science research data previously contributed to research databases may be retained for
scientific integrity.
</li>
</ul>
<h4 class="mt-4">5. Data Security</h4>
<p>
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.
</p>
<h4 class="mt-4">6. Children’s Privacy</h4>
<p>
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.
</p>
<h4 class="mt-4">7. Third-Party Links &amp; Services</h4>
<p>
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.
</p>
<h4 class="mt-4">8. Updates to This Privacy Policy</h4>
<p>
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.
</p>
<h4 class="mt-4">9. Contact Us</h4>
<p>
If you have any questions, concerns, or requests regarding this Privacy Policy or our data practices, please contact
us at:
</p>
<p class="mb-0">
<strong>WildeBackyard / Felidae Conservation Fund</strong>
<br>
<strong>Email:</strong> <a href="mailto:privacy@felidaefund.org">privacy@felidaefund.org</a> / <a href="mailto:support@felidaefund.org">support@felidaefund.org</a>
<br>
<strong>Mailing Address:</strong> 655 Redwood Hwy, Suite 150, Mill Valley, CA 94941
<br>
<strong>Website:</strong> <a href="https://felidaefund.org" target="_blank" rel="noopener">https://felidaefund.org</a> / <a href="https://wildebackyard.com" target="_blank" rel="noopener">https://wildebackyard.com</a>
</p>
Loading
Loading