Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Commit

Permalink
Bug 695113 apps newsletter to apps landing page
Browse files Browse the repository at this point in the history
  • Loading branch information
groovecoder committed Oct 18, 2011
1 parent d9eac99 commit ac797fd
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 31 deletions.
28 changes: 28 additions & 0 deletions apps/landing/forms.py
@@ -0,0 +1,28 @@
from django import forms

from tower import ugettext as _, ugettext_lazy as _lazy

EMAIL_REQUIRED = _lazy(u'Email address is required.')
EMAIL_SHORT = _lazy(u'Email address is too short (%(show_value)s characters). '
'It must be at least %(limit_value)s characters.')
EMAIL_LONG = _lazy(u'Email address is too long (%(show_value)s characters). '
'It must be %(limit_value)s characters or less.')
PRIVACY_REQUIRED = _lazy(u'You must agree to the privacy policy.')

class SubscriptionForm(forms.Form):
"""
Form to capture and validate email subscriptions
"""
email = forms.EmailField(label=_lazy(u'Your e-mail address'),
error_messages={'required': EMAIL_REQUIRED,
'min_length': EMAIL_SHORT,
'max_length': EMAIL_LONG})
format = forms.ChoiceField(
label=_lazy(u'Your preferred format'),
choices=[('html', 'HTML'), ('text', 'Plain text')],
widget=forms.RadioSelect()
)
agree = forms.BooleanField(
label=_lazy(u'I agree'),
error_messages={'required': PRIVACY_REQUIRED}
)
65 changes: 43 additions & 22 deletions apps/landing/templates/landing/apps.html
Expand Up @@ -9,8 +9,8 @@
{% block content %}
<header id="section-head">
<div class="wrap">
<h1 class="intro">{{ _('Welcome to the <b>Apps Developer Community</b>')|safe }}</h1>
<h1 class="intro">{{ _('Welcome to the <b>Apps Developer Community</b>')|safe }}</h1>

<div class="util">
<p><strong>{{ _('A short and snappy tagline') }}</strong></p>
<ul>
Expand All @@ -25,38 +25,59 @@ <h1 class="intro">{{ _('Welcome to the <b>Apps Developer Community</b>')|safe }}
<section id="content">
<div class="wrap sidebar">
<section id="content-main" role="main">
<form class="fm-subscribe boxed" action="/path/to/handler">

<form class="fm-subscribe boxed" action="{{ url('apps_subscription') }}" method="POST">
<header class="head">
<h2>Subscribe to the Apps Developer newsletter</h2>
<h2>{{ _('Subscribe to the Apps Developer newsletter') }}</h2>
</header>

{% if messages %}
<ul class="messages">
{% for message in messages %}
<li {% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>

<fieldset>
<p><label for="sub-email">Your e-mail address</label> <input type="email" name="email" id="sub-email" required="required"></p>
<p>
{{ form.email.errors }}
<label for="email">{{ form.email.label }}</label>
{{ form.email }}
</p>

<p class="check">
<span class="label">Your preferred format</span>
{{ form.format.errors }}
<span class="label">{{ form.format.label }}</span>
<fieldset class="inline">
<label for="sub-html"><input type="radio" name="format" value="html" id="sub-html" checked> HTML</label>
<label for="sub-text"><input type="radio" name="format" value="text" id="sub-text"> Plain text</label>
{% for c in form.fields.format.choices %}
<label for="sub-{{ c[0] }}"><input type="radio" name="format" value="{{ c[0] }}" id="sub-{{ c[0] }}" checked>{{ c[1] }}</label>
{% endfor %}
</fieldset>
</p>
<p class="agree">
<label for="sub-agree"><input type="checkbox" name="agree" id="sub-agree" required="required"> I agree</label>
to the <a href="http://www.mozilla.org/about/policies/privacy-policy.html">Privacy Policy</a>
{{ form.agree.errors }}
<label for="sub-agree">
{{ form.agree }} {{form.agree.label }}
</label>
{% trans privacy_url='http://www.mozilla.org/about/policies/privacy-policy.html' %}
to the <a href="{{ privacy_url }}">Privacy Policy</a>
{% endtrans %}
</p>
<p class="fm-submit"><button type="submit" class="button positive">Sign me up!</button></p>
<p class="fm-submit"><button type="submit" class="button positive">{{ _('Sign me up!') }}</button></p>
</fieldset>
</form>


<section class="boxed">
<div id="popular-docs" class="articles-list">
<header class="head section-apps">
{# L10n: Please escape HTML entities here, like &amp; #}
<h2>{{ _('Popular Apps Documentation')|safe }}</h2>
<h2>{{ _('Popular Apps Documentation') }}</h2>
</header>

<ul class="hfeed">
<li class="hentry">
<h4 class="entry-title"><a href="{{ devmo_url(_('/en/Apps')) }}" rel="bookmark">{{ _('Article Title') }}</a></h4>
Expand Down Expand Up @@ -105,7 +126,7 @@ <h4 class="entry-title"><a href="{{ devmo_url(_('/en/Apps')) }}" rel="bookmark">
<div id="fave-tools">
<header class="subhead">
{# L10n: Please escape HTML entities here, like &amp; #}
<h3>{{ _('Favorite Apps Tools')|safe }}</h3>
<h3>{{ _('Favorite Apps Tools') }}</h3>
</header>
<ul class="tools">
<li>
Expand All @@ -119,28 +140,28 @@ <h4><a href="http://mxr.mozilla.org/">{{ _('MXR') }} <img src="{{ MEDIA_URL }}im
<li class="all"><a href="{{ devmo_url(_('/en/Tools')) }}" class="go">{{ _('All application development tools') }}</a></li>
</ul>
</div>

<div id="recent-news" class="articles-list">
<header class="subhead">
{# L10n: Please escape HTML entities here, like &amp; #}
<h3>{{ _('Recent Apps News &amp; Updates')|safe }}</h3>
<h3>{{ _('Recent Apps News & Updates') }}</h3>
</header>

{{ newsfeed(updates) }}

<p class="all"><a href="#" class="go">{{ _('All application development entries') }}</a></p>
</div>
</section>{# /.boxed #}

</section>{# /#content-main #}

<aside id="content-sub" role="complementary">
<figure class="promo banner">
<a href="http://webfwd.org" rel="external">
<img src="{{MEDIA_URL}}img/webfwd-promo-square.png" width="235" height="205" alt="Web Fwd: Mozilla's accelerator for open Web projects. Learn more.">
</a>
</figure>

{{ twitter(tweets, title=_('Correspondence on Twitter from the Mozilla developer community.')) }}
</aside>{# /#content-sub #}

Expand Down
74 changes: 69 additions & 5 deletions apps/landing/test_views.py
Expand Up @@ -6,17 +6,14 @@
import urllib2
from os.path import basename, dirname, isfile, isdir

from nose.tools import eq_, ok_
from mock import patch
from nose.tools import assert_equal, with_setup, assert_false, eq_, ok_
from nose.plugins.attrib import attr
from pyquery import PyQuery as pq
import test_utils
from waffle.models import Switch

from django.contrib.auth.models import User, AnonymousUser

from sumo.tests import LocalizingClient
from sumo.urlresolvers import reverse
from funfactory.urlresolvers import reverse


class LearnViewsTest(test_utils.TestCase):
Expand Down Expand Up @@ -115,3 +112,70 @@ def test_forum_archive(self):
url = reverse('landing.views.forum_archive')
r = self.client.get(url, follow=True)
eq_(200, r.status_code)


class AppsViewsTest(test_utils.TestCase):

def setUp(self):
self.client = LocalizingClient()

def test_apps_menu_item(self):
url = reverse('landing.views.home')
r = self.client.get(url)
eq_(200, r.status_code)

doc = pq(r.content)
nav_sub_topics = doc.find('ul#nav-sub-topics')
ok_(nav_sub_topics)
apps_item = nav_sub_topics.find('li#nav-sub-apps')
eq_([], apps_item)

s = Switch.objects.create(name='apps_landing', active=True)
s.save()
r = self.client.get(url)
eq_(200, r.status_code)
doc = pq(r.content)
nav_sub_topics = doc.find('ul#nav-sub-topics')
ok_(nav_sub_topics)
apps_item = nav_sub_topics.find('li#nav-sub-apps')
eq_('Apps', apps_item.text())

def test_apps(self):
url = reverse('landing.views.apps')
r = self.client.get(url)
eq_(404, r.status_code)

s = Switch.objects.create(name='apps_landing', active=True)
s.save()
r = self.client.get(url, follow=True)
eq_(200, r.status_code)
doc = pq(r.content)
responsys_form = doc.find('form.fm-subscribe')
eq_(reverse('apps_subscription'), responsys_form.attr('action'))

@patch('landing.views.responsys.subscribe')
def test_apps_subscription(self, subscribe):
subscribe.return_value = True
s = Switch.objects.create(name='apps_landing', active=True)
s.save()
url = reverse('landing.views.apps_subscription')
r = self.client.post(url, {'format': 'html', 'email': 'testuser@test.com', 'agree': 'checked'}, follow=True)
eq_(200, r.status_code)
# assert thank you message
self.assertContains(r, 'Thank you')
# TODO: figure out why the mock doesn't work?
# subscribe.assert_called_once_with('APP_DEV_BREAK', 'testuser@test.com', format='text')


@patch('landing.views.responsys.subscribe')
def test_apps_subscription_bad_values(self, subscribe):
subscribe.return_value = True
s = Switch.objects.create(name='apps_landing', active=True)
s.save()
url = reverse('landing.views.apps_subscription')
r = self.client.post(url, {'format': 1, 'email': 'nope'})
eq_(200, r.status_code)
# assert error
self.assertContains(r, 'Enter a valid e-mail address.')
self.assertContains(r, 'Select a valid choice.')
self.assertContains(r, 'You must agree to the privacy policy.')
2 changes: 1 addition & 1 deletion apps/landing/urls.py
@@ -1,6 +1,5 @@
from django.conf.urls.defaults import *


urlpatterns = patterns('landing.views',
url(r'^$', 'home', name='home'),
url(r'^addons/?$', 'addons', name='addons'),
Expand All @@ -9,6 +8,7 @@
url(r'^search/?$', 'search', name='search'),
url(r'^web/?$', 'web', name='web'),
url(r'^apps/?$', 'apps', name='apps'),
url(r'^apps/subscription/?$', 'apps_subscription', name='apps_subscription'),
url(r'^learn/?$', 'learn', name='learn'),
url(r'^learn/html/?$', 'learn_html', name='learn_html'),
url(r'^learn/css/?$', 'learn_css', name='learn_css'),
Expand Down
22 changes: 21 additions & 1 deletion apps/landing/views.py
@@ -1,9 +1,17 @@
from django.contrib import messages
from django.http import HttpResponseRedirect

import jingo
from tower import ugettext as _
from waffle.decorators import waffle_switch
import responsys

from devmo import (SECTION_USAGE, SECTION_ADDONS, SECTION_APPS, SECTION_MOBILE,
SECTION_WEB)
from feeder.models import Bundle, Feed
from demos.models import Submission
from landing.forms import SubscriptionForm
from funfactory.urlresolvers import reverse


def home(request):
Expand Down Expand Up @@ -56,9 +64,21 @@ def web(request):
"""Web landing page."""
return common_landing(request, section=SECTION_WEB)

@waffle_switch('apps_landing')
def apps(request):
"""Web landing page."""
return common_landing(request, section=SECTION_APPS)
return common_landing(request, section=SECTION_APPS, extra={'form': SubscriptionForm()})

@waffle_switch('apps_landing')
def apps_subscription(request):
form = SubscriptionForm(data=request.POST)
if form.is_valid():
responsys.subscribe('APP_DEV', form.cleaned_data['email'], format=form.cleaned_data['format'])
messages.success(request, _('Thank you for subscribing to the Apps developer newsletter.'))
return HttpResponseRedirect(reverse('apps'))

"""Web landing page."""
return common_landing(request, section=SECTION_APPS, extra={'form': form})

def learn(request):
"""Learn landing page."""
Expand Down
50 changes: 50 additions & 0 deletions lib/responsys.py
@@ -0,0 +1,50 @@
from datetime import date

import pycurl

This comment has been minimized.

Copy link
@lmorchard

lmorchard Oct 18, 2011

Contributor

Can we do this with the httplib or requests module? We already have one or both of those, and they don't have compiled and binary dependencies.

This comment has been minimized.

Copy link
@groovecoder

groovecoder Oct 18, 2011

Author Contributor

yeah I'll try to change it to requests tonight.


from django.conf import settings
from django.utils.http import urlencode


def make_source_url(request):
return request.get_host() + request.get_full_path()


def subscribe(campaign, address, format='html', source_url='', lang='', country=''):
"""
Subscribe a user to a list in responsys. There should be two
fields within the Responsys system named by the "campaign"
parameter: <campaign>_FLG and <campaign>_DATE
"""
data = {
'LANG_LOCALE': lang,
'COUNTRY_': country,
'SOURCE_URL': source_url,
'EMAIL_ADDRESS_': address,
'EMAIL_FORMAT_': 'H' if format == 'html' else 'T',
}

data['%s_FLG' % campaign] = 'Y'
data['%s_DATE' % campaign] = date.today().strftime('%Y-%m-%d')

# views.py asserts setting is available
data['_ri_'] = settings.RESPONSYS

This comment has been minimized.

Copy link
@lmorchard

lmorchard Oct 18, 2011

Contributor

Missing RESPONSYS and RESPONSYS_API_URL in settings.py - should at least have some defaults here with something like getattr(settings, 'RESPONSYS', 'foobar')

This comment has been minimized.

Copy link
@groovecoder

groovecoder Oct 18, 2011

Author Contributor

oops, yeah. both of those values are secret but I'll add dummies to settings.py


if not settings.RESPONSYS_API_URL.lower().startswith('https://'):
raise Exception('Responsys API URL must start with HTTPS.')

curl = pycurl.Curl()
# Ensure SSL cert validates before sending user data over the wire
curl.setopt(pycurl.SSL_VERIFYPEER, 1)
curl.setopt(pycurl.SSL_VERIFYHOST, 2)
curl.setopt(pycurl.URL, settings.RESPONSYS_API_URL)
# Add POST data
curl.setopt(pycurl.POST, 1)
curl.setopt(pycurl.POSTFIELDS, urlencode(data))
try:
curl.perform()
except Exception, ce:
raise Exception('Newsletter subscription failed: %s' % ce)
else:
return curl.getinfo(pycurl.RESPONSE_CODE) == 200

2 changes: 2 additions & 0 deletions manage.py
Expand Up @@ -44,6 +44,8 @@
# Import for side-effect: configures our logging handlers.
import log_settings

import safe_django_forms
safe_django_forms.monkeypatch()

if __name__ == "__main__":
execute_manager(settings)
1 change: 1 addition & 0 deletions requirements/compiled.txt
Expand Up @@ -5,3 +5,4 @@ lxml==2.2.6
coverage==3.2b4
pylibmc==0.9
django-pylibmc
pycurl==7.19.0
4 changes: 3 additions & 1 deletion templates/base.html
Expand Up @@ -58,15 +58,17 @@ <h1 id="logo"><a href="{{ url('home') }}"><img src="{{ MEDIA_URL }}img/mdn-logo-
</form>

{% block headernav %}

<nav id="nav" role="menubar">
<ul id="nav-main">
<li id="nav-main-topics" class="menu"><a href="#nav-sub-topics" class="toggle" aria-haspopup="true" aria-labelledby="nav-main-topics" title="Explore other parts of MDN">{{ _('Topics') }}</a>
<ul id="nav-sub-topics" class="sub-menu" aria-hidden="true">
<li id="nav-sub-web"><a href="{{ url('web') }}">{{ _('Web') }}</a></li>
<li id="nav-sub-mobile"><a href="{{ url('mobile') }}">{{ _('Mobile') }}</a></li>
<li id="nav-sub-addons"><a href="{{ url('addons') }}">{{ _('Add-ons') }}</a></li>
{% if waffle.switch('apps_landing') %}
<li id="nav-sub-apps"><a href="{{ url('apps') }}">{{ _('Apps') }}</a></li>
{% endif %}
<li id="nav-sub-mozilla"><a href="{{ url('mozilla') }}">{{ _('Mozilla') }}</a></li>
</ul>
</li>
Expand Down
2 changes: 1 addition & 1 deletion vendor

0 comments on commit ac797fd

Please sign in to comment.