Some ad changes around opting out. (#2951)

* Allow users to opt out of ads

* Small update to verbage

* Properly filter opted out users

* Add boolean instead for community ads.

* Add opted out projects as community only as well

* Small wording change

* Make user opt out follow same defaults/verbage as projects
com-py3-compat
Eric Holscher 2017-06-15 15:58:36 -07:00 committed by Anthony
parent 7d586be5e7
commit 053b54d64d
8 changed files with 96 additions and 8 deletions

View File

@ -22,7 +22,7 @@ class UserProfileForm(forms.ModelForm):
class Meta(object):
model = UserProfile
# Don't allow users edit someone else's user page,
fields = ['first_name', 'last_name', 'homepage']
fields = ['first_name', 'last_name', 'homepage', 'allow_ads']
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-06-14 18:06
from __future__ import unicode_literals
import annoying.fields
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0003_add_banned_status'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='allow_ads',
field=models.BooleanField(default=True, help_text='If unchecked, you will still see community ads.', verbose_name='See paid advertising'),
),
migrations.AlterField(
model_name='userprofile',
name='user',
field=annoying.fields.AutoOneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL, verbose_name='User'),
),
]

View File

@ -23,6 +23,10 @@ class UserProfile (models.Model):
whitelisted = models.BooleanField(_('Whitelisted'), default=False)
banned = models.BooleanField(_('Banned'), default=False)
homepage = models.CharField(_('Homepage'), max_length=100, blank=True)
allow_ads = models.BooleanField(_('See paid advertising'),
help_text=_('If unchecked, you will still see community ads.'),
default=True,
)
allow_email = models.BooleanField(_('Allow email'),
help_text=_('Show your email on VCS '
'contributions.'),

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-06-14 17:48
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('donate', '0011_add-theme-filter'),
]
operations = [
migrations.AddField(
model_name='supporterpromo',
name='community',
field=models.BooleanField(default=False, verbose_name='Community Ad'),
),
]

View File

@ -70,6 +70,7 @@ class SupporterPromo(models.Model):
theme = models.CharField(_('Theme'), max_length=40,
choices=THEMES, default=READTHEDOCS_THEME,
blank=True, null=True)
community = models.BooleanField(_('Community Ad'), default=False)
live = models.BooleanField(_('Live'), default=False)
class Meta(object):

View File

@ -90,7 +90,8 @@ def choose_promo(promo_list):
return None
def get_promo(country_code, programming_language, theme, gold_project=False, gold_user=False):
def get_promo(country_code, programming_language, theme,
gold_project=False, gold_user=False, community_only=False):
"""
Get a proper promo.
@ -104,6 +105,9 @@ def get_promo(country_code, programming_language, theme, gold_project=False, gol
"""
promo_queryset = SupporterPromo.objects.filter(live=True, display_type='doc')
if community_only:
promo_queryset = promo_queryset.filter(community=True)
filtered_promos = []
for promo in promo_queryset:
# Break out if we aren't meant to show to this language
@ -157,6 +161,15 @@ def is_gold_project(project):
return project.gold_owners.count()
def is_community_only(user, project):
"""Return True is this project or user should only be shown community ads"""
if user.is_authenticated() and user.profile.as_opt_out:
return True
if not project.allow_promos:
return True
return False
def get_user_country(request):
"""Return the ISO country code from geo-IP data, or None if not found."""
if not PROMO_GEO_PATH:
@ -200,11 +213,9 @@ def lookup_promo(request, project, theme):
if no promo should be shown.
"""
if not project.allow_promos:
return None
gold_user = is_gold_user(request.user)
gold_project = is_gold_project(project)
community_only = is_community_only(request.user, project)
# Don't show promos to gold users or on gold projects for now
# (Some day we may show them something customised for them)
@ -217,6 +228,7 @@ def lookup_promo(request, project, theme):
theme=theme,
gold_project=gold_project,
gold_user=gold_user,
community_only=community_only,
)
# If we don't have anything to show, don't show it.

View File

@ -4,13 +4,14 @@ import json
import mock
from builtins import range
from django.contrib.auth.models import User
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.core.cache import cache
from django.test.client import RequestFactory
from django_dynamic_fixture import get
from readthedocs.core.middleware import FooterNoSessionMiddleware
from .core.middleware import FooterNoSessionMiddleware
from .models import SupporterPromo, GeoFilter, Country
from .constants import (CLICKS, VIEWS, OFFERS,
INCLUDE, EXCLUDE)
@ -184,6 +185,28 @@ class FooterTests(TestCase):
resp = json.loads(r.content)
self.assertEqual(resp['promo'], False)
def test_user_disabling(self):
"""Test that the promo doesn't show when the project has it disabled"""
user = User.objects.get(username='test')
user.profile.allow_ads = False
user.profile.save()
# No ads for logged in user
self.login('test', 'test')
r = self.client.get(
'/api/v2/footer_html/?project=pip&version=latest&page=index'
)
resp = json.loads(r.content)
self.assertEqual(resp['promo'], False)
# Ads for logged out user
self.logout()
r = self.client.get(
'/api/v2/footer_html/?project=pip&version=latest&page=index'
)
resp = json.loads(r.content)
self.assertTrue(resp['promo'] is not False)
class FilterTests(TestCase):

View File

@ -158,8 +158,8 @@ class Project(models.Model):
build_queue = models.CharField(
_('Alternate build queue id'), max_length=32, null=True, blank=True)
allow_promos = models.BooleanField(
_('Sponsor advertisements'), default=True, help_text=_(
"Allow sponsor advertisements on my project documentation"))
_('Allow paid advertising'), default=True, help_text=_(
"If unchecked, users will still see community ads."))
# Sphinx specific build options.
enable_epub_build = models.BooleanField(