Skip to content

Commit

Permalink
Merge pull request #4046 from davidfischer/do-not-track-support
Browse files Browse the repository at this point in the history
Do Not Track support
  • Loading branch information
ericholscher authored May 30, 2018
2 parents f083818 + e01d94c commit d7fceb5
Show file tree
Hide file tree
Showing 11 changed files with 393 additions and 57 deletions.
26 changes: 22 additions & 4 deletions docs/advertising-details.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,23 @@ However, we always give advance notice in our issue tracker
and via email about showing ads where none were shown before.


.. _advertising-details-do-not-track:

Do Not Track Policy
-------------------

Read the Docs supports Do Not Track (DNT) and respects users' tracking preferences.
For more details, see the :ref:`Do Not Track section <privacy-policy-do-not-track>`
of our privacy policy.


.. _advertising-analytics:

Analytics
---------

Analytics are a sensitive enough issue that they require their own section.
In the spirit of full transparency, Read the Docs currently uses Google Analytics (GA).
In the spirit of full transparency, Read the Docs uses Google Analytics (GA).
We go into a bit of detail on our use of GA in our :doc:`privacy-policy`.

GA is a contentious issue inside Read the Docs and in our community.
Expand All @@ -126,14 +136,22 @@ The developers at Read the Docs understand that different users have different p
and we try to respect the different viewpoints as much as possible while also accomplishing
our own goals.

We have taken steps to address some of the privacy concerns surrounding GA.
These steps apply both to analytics collected by Read the Docs and when
:doc:`authors enable analytics on their docs <guides/google-analytics>`.

* Users can opt-out of analytics by using the Do Not Track feature of their browser.
* Read the Docs instructs Google to anonymize IP addresses sent to them.
* The cookies set by GA expire in 30 days rather than the default 2 years.

Why we use analytics
~~~~~~~~~~~~~~~~~~~~

Advertisers ask us questions that are easily answered with an analytics solution like
"how many users do you have in Switzerland browsing Python docs?". We need to be able
to easily get this data. We also use data from GA for some development decisions such
as what browsers to support (or not) or how much usage a particular page or feature gets.

We have taken steps to address some of the privacy concerns.
Read the Docs instructs Google to anonymize IPs sent to them before they are stored.

Alternatives
~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion docs/ethical-advertising.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Additional details

* We have additional documentation on the
:doc:`technical details of our advertising <advertising-details>`
including our use of analytics.
including our Do Not Track policy and our use of analytics.
* We have an `advertising FAQ`_ written for advertisers.
* We have gone into more detail about our views in our
`blog post <https://blog.readthedocs.com/ads-on-read-the-docs/>`_ about this topic.
Expand Down
12 changes: 11 additions & 1 deletion docs/guides/google-analytics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@ You can enable it by:

Once your documentation rebuilds it will include your Analytics tracking code and start sending data.
Google Analytics usually takes 60 minutes,
and sometimes can take up to a day before it starts reporting data.
and sometimes can take up to a day before it starts reporting data.

.. note::

Read the Docs takes some extra precautions with analytics to protect user privacy.
As a result, users with Do Not Track enabled will not be counted
for the purpose of analytics.

For more details, see the
:ref:`Do Not Track section <privacy-policy-do-not-track>`
of our privacy policy.
42 changes: 39 additions & 3 deletions docs/privacy-policy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ readthedocs.com
It is governed by this privacy policy but also separate
`terms <https://readthedocs.com/terms/>`_.

readthedocs.io and other domains ("Documentation Sites")
These public websites are where Read the Docs hosts documentation on
readthedocs.io, readthedocs-hosted.com, and other domains ("Documentation Sites")
These websites are where Read the Docs hosts documentation on
behalf of documentation authors. A best effort is made to apply
this Privacy Policy to these sites but the documentation
may contain content and files created by documentation authors.
Expand Down Expand Up @@ -223,6 +223,41 @@ We do not sell that content; it is yours.
Our use of cookies and tracking
-------------------------------

.. _privacy-policy-do-not-track:

Do Not Track
~~~~~~~~~~~~

Read the Docs supports Do Not Track (DNT) and respects users' tracking preferences.
Specifically, we support the `W3C's tracking preference expression`_
and the `EFF's DNT Policy`_.

For Read the Docs, this means:

* We **do not** do behavioral ad targeting regardless of your DNT preference.
* When DNT is enabled, both logged-in and logged-out users
are considered opted-out of :ref:`analytics <privacy-policy-analytics>`.
* Regardless of DNT preference, our logs that contain IP addresses
and user agent strings are deleted after 10 days unless a DNT exception applies.
* Our full DNT policy is `available here`_.

Our DNT policy applies without reservation to readthedocs.org and readthedocs.com.
A best effort is made to apply this to Documentation Sites,
but we do not have complete control over the contents of these sites.

For more details about DNT, visit `All About Do Not Track`_.

.. important::

Due to the nature of our environment where documentation is built as necessary,
the DNT analytics opt-out for Documentation Sites only applies
for those sites generated after May 1, 2018.

.. _W3C's tracking preference expression: https://www.w3.org/TR/tracking-dnt/
.. _EFF's DNT Policy: https://www.eff.org/issues/do-not-track
.. _available here: https://readthedocs.org/.well-known/dnt-policy.txt
.. _All About Do Not Track: http://www.allaboutdnt.com

Cookies
~~~~~~~

Expand Down Expand Up @@ -261,6 +296,7 @@ collect any User Personal Information other than IP address;
or correlate your IP address with your identity.
Google provides further information about its own privacy practices and offers a
`browser add-on to opt out of Google Analytics tracking <https://tools.google.com/dlpage/gaoptout>`_.
You may also opt-out of analytics on Read the Docs by enabled Do Not Track.


How Read the Docs secures your information
Expand Down Expand Up @@ -352,7 +388,7 @@ We will retain and use your information as necessary to comply with
our legal obligations, resolve disputes, and enforce our agreements,
but barring legal requirements, we will delete your full profile.

Our web server logs for both readthedocs.org and documentation sites
Our web server logs for readthedocs.org, readthedocs.com, and Documentation Sites
are deleted after 10 days barring legal obligations.


Expand Down
59 changes: 33 additions & 26 deletions media/javascript/readthedocs-analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,40 @@
// https://docs.readthedocs.io/en/latest/advertising-details.html#analytics


// RTD Analytics Code
// Skip analytics for users with Do Not Track enabled
if (navigator.doNotTrack === '1') {
console.log('Respecting DNT with respect to analytics...');
} else {
// RTD Analytics Code
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
if (typeof READTHEDOCS_DATA !== 'undefined') {
if (READTHEDOCS_DATA.global_analytics_code) {
ga('create', READTHEDOCS_DATA.global_analytics_code, 'auto', 'rtfd', {
'cookieExpires': 30 * 24 * 60 * 60
});
ga('rtfd.set', 'dimension1', READTHEDOCS_DATA.project);
ga('rtfd.set', 'dimension2', READTHEDOCS_DATA.version);
ga('rtfd.set', 'dimension3', READTHEDOCS_DATA.language);
ga('rtfd.set', 'dimension4', READTHEDOCS_DATA.theme);
ga('rtfd.set', 'dimension5', READTHEDOCS_DATA.programming_language);
ga('rtfd.set', 'dimension6', READTHEDOCS_DATA.builder);
ga('rtfd.set', 'anonymizeIp', true);
ga('rtfd.send', 'pageview');
}

if (typeof READTHEDOCS_DATA !== 'undefined') {
if (READTHEDOCS_DATA.global_analytics_code) {
ga('create', READTHEDOCS_DATA.global_analytics_code, 'auto', 'rtfd');
ga('rtfd.set', 'dimension1', READTHEDOCS_DATA.project);
ga('rtfd.set', 'dimension2', READTHEDOCS_DATA.version);
ga('rtfd.set', 'dimension3', READTHEDOCS_DATA.language);
ga('rtfd.set', 'dimension4', READTHEDOCS_DATA.theme);
ga('rtfd.set', 'dimension5', READTHEDOCS_DATA.programming_language);
ga('rtfd.set', 'dimension6', READTHEDOCS_DATA.builder);
ga('rtfd.set', 'anonymizeIp', true);
ga('rtfd.send', 'pageview');
// User Analytics Code
if (READTHEDOCS_DATA.user_analytics_code) {
ga('create', READTHEDOCS_DATA.user_analytics_code, 'auto', 'user', {
'cookieExpires': 30 * 24 * 60 * 60
});
ga('user.set', 'anonymizeIp', true);
ga('user.send', 'pageview');
}
// End User Analytics Code
}

// User Analytics Code
if (READTHEDOCS_DATA.user_analytics_code) {
ga('create', READTHEDOCS_DATA.user_analytics_code, 'auto', 'user');
ga('user.set', 'anonymizeIp', true);
ga('user.send', 'pageview');
}
// End User Analytics Code
// end RTD Analytics Code
}

// end RTD Analytics Code
1 change: 1 addition & 0 deletions readthedocs/core/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def readthedocs_processor(request):
'DASHBOARD_ANALYTICS_CODE': getattr(settings, 'DASHBOARD_ANALYTICS_CODE'),
'SITE_ROOT': getattr(settings, 'SITE_ROOT', '') + '/',
'TEMPLATE_ROOT': getattr(settings, 'TEMPLATE_ROOT', '') + '/',
'DO_NOT_TRACK_ENABLED': getattr(settings, 'DO_NOT_TRACK_ENABLED', False),
'USE_PROMOS': getattr(settings, 'USE_PROMOS', False),
}
return exports
18 changes: 17 additions & 1 deletion readthedocs/core/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import logging

from django.conf import settings
from django.http import HttpResponseRedirect, Http404
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView
Expand Down Expand Up @@ -121,3 +121,19 @@ def server_error_404(request, exception=None, template_name='404.html'): # pyli
r = render(request, template_name)
r.status_code = 404
return r


def do_not_track(request):
dnt_header = request.META.get('HTTP_DNT')

# https://w3c.github.io/dnt/drafts/tracking-dnt.html#status-representation
return JsonResponse({ # pylint: disable=redundant-content-type-for-json-response
'policy': 'https://docs.readthedocs.io/en/latest/privacy-policy.html',
'same-party': [
'readthedocs.org',
'readthedocs.com',
'readthedocs.io', # .org Documentation Sites
'readthedocs-hosted.com', # .com Documentation Sites
],
'tracking': 'N' if dnt_header == '1' else 'T',
}, content_type='application/tracking-status+json')
4 changes: 4 additions & 0 deletions readthedocs/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class CommunityBaseSettings(Settings):
SESSION_COOKIE_DOMAIN = 'readthedocs.org'
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_AGE = 30 * 24 * 60 * 60

# Application classes
@property
Expand Down Expand Up @@ -330,6 +331,9 @@ def USE_PROMOS(self): # noqa
STRIPE_SECRET = None
STRIPE_PUBLISHABLE = None

# Do Not Track support
DO_NOT_TRACK_ENABLED = False

# Misc application settings
GLOBAL_ANALYTICS_CODE = None
DASHBOARD_ANALYTICS_CODE = None # For the dashboard, not docs
Expand Down
42 changes: 26 additions & 16 deletions readthedocs/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,32 @@

<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', '{{ GLOBAL_ANALYTICS_CODE }}', 'auto', 'rtfd');
ga('rtfd.set', 'anonymizeIp', true);
ga('rtfd.send', 'pageview');

{% if DASHBOARD_ANALYTICS_CODE %}
// Dashboard Analytics Code
ga('create', '{{ DASHBOARD_ANALYTICS_CODE }}', 'auto', 'user');
ga('user.set', 'anonymizeIp', true);
ga('user.send', 'pageview');
// End Dashboard Analytics Code
{% endif %}
if ({{ DO_NOT_TRACK_ENABLED | lower }} && navigator.doNotTrack === '1') {
console.log('Respecting DNT with respect to analytics...');
} else {
// For more details on analytics at Read the Docs, please see:
// https://docs.readthedocs.io/en/latest/advertising-details.html#analytics
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', '{{ GLOBAL_ANALYTICS_CODE }}', 'auto', 'rtfd', {
'cookieExpires': 30 * 24 * 60 * 60
});
ga('rtfd.set', 'anonymizeIp', true);
ga('rtfd.send', 'pageview');

{% if DASHBOARD_ANALYTICS_CODE %}
// Dashboard Analytics Code
ga('create', '{{ DASHBOARD_ANALYTICS_CODE }}', 'auto', 'user', {
'cookieExpires': 30 * 24 * 60 * 60
});
ga('user.set', 'anonymizeIp', true);
ga('user.send', 'pageview');
// End Dashboard Analytics Code
{% endif %}
}
</script>
<!-- End Google Analytics -->

Expand Down
Loading

0 comments on commit d7fceb5

Please sign in to comment.