Skip to content
This repository has been archived by the owner on Jan 31, 2018. It is now read-only.

Commit

Permalink
[bug 935632] Add intermediary account creation
Browse files Browse the repository at this point in the history
This creates a UserProfile model which doesn't do a whole lot yet other
than let us denote new users.

This updates all the places where the user's name shows up to use
get_display_name().

This adds a FjordVerify class which sends new users to the creation
page. Right now this is sort of a stub and allows us to create a
UserProfile. At some point p, this will be important and will have a
profile creation form and also tell the new user what he/she can do on
Input.
  • Loading branch information
willkg committed Nov 14, 2013
1 parent 7200d99 commit 99cc5ca
Show file tree
Hide file tree
Showing 18 changed files with 299 additions and 16 deletions.
Expand Up @@ -12,7 +12,7 @@
<div class="col wide">
{% block content_middle %}
<div class="block feedback">
<h2>Welcome Analyzer {{ user.first_name }}!</h2>
<h2>Welcome Analyzer {{ displayname(user) }}!</h2>
<p>
Coming soon in 2013q4!
</p>
Expand Down
10 changes: 5 additions & 5 deletions fjord/analytics/templates/analytics/spam_dashboard.html
Expand Up @@ -18,11 +18,11 @@ <h2>Reports</h2>
<div class="col wide">
{% block content_middle %}
<div class="block">
<h2>Greetings!</h2>
<p>
Greetings, Spamfighter {{ user.username }}. You have been recruited by the Star
League to defend the frontier against Xur and the Ko-Dan armada.
</p>
<h2>
Greetings, Spamfighter {{ displayname(user) }}! You
have been recruited by the Star League to defend the frontier against
Xur and the Ko-Dan armada.
</h2>
</div>
{% endblock %}
</div>
Expand Down
4 changes: 3 additions & 1 deletion fjord/analytics/tests/test_views.py
Expand Up @@ -11,7 +11,7 @@

from fjord.analytics import views
from fjord.analytics.views import counts_to_options, _zero_fill
from fjord.base.tests import TestCase, LocalizingClient, reverse, user
from fjord.base.tests import TestCase, LocalizingClient, profile, reverse, user
from fjord.base.util import epoch_milliseconds
from fjord.feedback.tests import response
from fjord.search.tests import ElasticTestCase
Expand Down Expand Up @@ -450,6 +450,7 @@ def test_permissions(self):

# Verify analyzers can see analytics dashboard link
jane = user(email='jane@example.com', save=True)
profile(user=jane, save=True)
jane.groups.add(Group.objects.get(name='analyzers'))

self.client_login_user(jane)
Expand Down Expand Up @@ -477,6 +478,7 @@ def test_permissions(self):

# Verify analyzers can see spam dashboard link
jane = user(email='jane@example.com', save=True)
profile(user=jane, save=True)
jane.groups.add(Group.objects.get(name='analyzers'))

self.client_login_user(jane)
Expand Down
14 changes: 10 additions & 4 deletions fjord/analytics/views.py
Expand Up @@ -2,7 +2,6 @@
from datetime import datetime, timedelta
from math import floor

from django.contrib.auth.decorators import permission_required
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from django.template.defaultfilters import slugify
Expand All @@ -15,6 +14,8 @@
from fjord.analytics.tools import JSONDatetimeEncoder, generate_query_parsed
from fjord.base.helpers import locale_name, to_datetime_string
from fjord.base.util import (
analyzer_required,
check_new_user,
smart_int,
smart_datetime,
epoch_milliseconds,
Expand Down Expand Up @@ -143,6 +144,7 @@ def _zero_fill(start, end, data_sets, spacing=DAY_IN_MILLIS):
timestamp += spacing


@check_new_user
@mobile_template('analytics/{mobile/}response.html')
def response_view(request, responseid, template):
response = get_object_or_404(Response, id=responseid)
Expand Down Expand Up @@ -230,6 +232,7 @@ def generate_dashboard_atom_url(request):
return reverse('dashboard') + '?' + qd.urlencode()


@check_new_user
@es_required_or_50x(error_template='analytics/es_down.html')
@mobile_template('analytics/{mobile/}dashboard.html')
def dashboard(request, template):
Expand Down Expand Up @@ -439,21 +442,24 @@ def empty_to_unknown(text):
})


@permission_required('analytics.can_view_dashboard', raise_exception=True)
@check_new_user
@analyzer_required
@es_required_or_50x(error_template='analytics/es_down.html')
@mobile_template('analytics/{mobile/}analytics_dashboard.html')
def analytics_dashboard(request, template):
return render(request, template)


@permission_required('analytics.can_view_dashboard', raise_exception=True)
@check_new_user
@analyzer_required
@es_required_or_50x(error_template='analytics/es_down.html')
@mobile_template('analytics/{mobile/}spam_dashboard.html')
def spam_dashboard(request, template):
return render(request, template)


@permission_required('analytics.can_view_dashboard', raise_exception=True)
@check_new_user
@analyzer_required
@es_required_or_50x(error_template='analytics/es_down.html')
def spam_duplicates(request):
"""Shows all duplicate descriptions over the last n days"""
Expand Down
35 changes: 35 additions & 0 deletions fjord/base/browserid.py
@@ -0,0 +1,35 @@
import urlparse

from django.http import HttpResponseRedirect

from django_browserid.views import Verify
from funfactory.urlresolvers import reverse

from fjord.base.models import Profile


class FjordVerify(Verify):
def login_success(self):
"""Send to new-user-view if new user, otherwise send on their way"""
response = super(FjordVerify, self).login_success()
# If this user has never logged in before, send them to our
# super secret "Welcome!" page.
try:
self.user.profile
return response

except Profile.DoesNotExist:
url = reverse('new-user-view')

redirect_to = self.request.REQUEST.get('next')

# Do not accept redirect URLs pointing to a different host.
if redirect_to:
netloc = urlparse.urlparse(redirect_to).netloc
if netloc and netloc != self.request.get_host():
redirect_to = None

if redirect_to:
url = url + '?next=' + redirect_to

return HttpResponseRedirect(url)
6 changes: 6 additions & 0 deletions fjord/base/helpers.py
Expand Up @@ -59,3 +59,9 @@ def to_datetime_string(dt):
def to_date_string(dt):
"""Converts date/datetime to '%Y-%m-%d'"""
return dt.strftime('%Y-%m-%d')


@register.function
def displayname(user):
"""Returns the best display name for the user"""
return user.first_name or user.email
68 changes: 68 additions & 0 deletions fjord/base/migrations/0002_auto__add_profile.py
@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):
# Adding model 'Profile'
db.create_table('base_profile', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
))
db.send_create_signal('base', ['Profile'])


def backwards(self, orm):
# Deleting model 'Profile'
db.delete_table('base_profile')


models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'base.profile': {
'Meta': {'object_name': 'Profile'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}

complete_apps = ['base']
5 changes: 5 additions & 0 deletions fjord/base/models.py
@@ -1,3 +1,4 @@
from django.contrib.auth.models import User
from django.db import models

import caching.base
Expand All @@ -11,3 +12,7 @@ class ModelBase(caching.base.CachingMixin, models.Model):

class Meta:
abstract = True


class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
2 changes: 1 addition & 1 deletion fjord/base/templates/base.html
Expand Up @@ -41,7 +41,7 @@ <h1 class="title"><a href="/">

<ul class="pull-right">
{% if user.is_authenticated() %}
<li><span class="greeting">{{ _('Hello, {username}')|fe(username=user.first_name) }}</span></li>
<li><span class="greeting">{{ _('Hello, {username}')|fe(username=displayname(user)) }}</span></li>
{% if user.is_superuser %}
<li><span><a href="{{ url('admin:index') }}">Admin</a></span></li>
{% endif %}
Expand Down
10 changes: 10 additions & 0 deletions fjord/base/templates/includes/new_user_text.html
@@ -0,0 +1,10 @@
<h2>{{ _('Welcome!') }}</h2>
<p>
{% trans %}
The identity you just logged into Persona with ties you to your
account on Input. Please remember which identity you used.
{% endtrans %}
</p>
<p>
<a href="{{ next_url }}">{{ _('Onwards to your destination.') }}</a>
</p>
11 changes: 11 additions & 0 deletions fjord/base/templates/mobile/new_user.html
@@ -0,0 +1,11 @@
{% extends "mobile/base.html" %}

{% block page_title %}{{ _('Welcome to Input!') }}{% endblock %}

{% block content %}
<article>
<section>
{% include "includes/new_user_text.html" %}
</section>
</article>
{% endblock %}
12 changes: 12 additions & 0 deletions fjord/base/templates/new_user.html
@@ -0,0 +1,12 @@
{% extends "base.html" %}

{% block page_title %}{{ _('Welcome to Input!') }}{% endblock %}

{% block content %}
<div class="col"></div>
<div class="col wide">
<div class="block">
{% include "includes/new_user_text.html" %}
</div>
</div>
{% endblock %}
14 changes: 14 additions & 0 deletions fjord/base/tests/__init__.py
Expand Up @@ -20,6 +20,8 @@
# here rather than importing it from funfactory
from funfactory.urlresolvers import split_path, reverse

from fjord.base.models import Profile


class LocalizingClient(Client):
"""Client which rewrites urls to include locales and adds a user agent.
Expand Down Expand Up @@ -148,3 +150,15 @@ def user(**kwargs):
user = User(**defaults)
user.set_password(kwargs.get('password', 'testpass'))
return user


@with_save
def profile(**kwargs):
"""Returns a Profile"""
defaults = {}
if 'user' not in kwargs:
defaults['user'] = user(save=True)

defaults.update(kwargs)

return Profile(**defaults)
73 changes: 73 additions & 0 deletions fjord/base/tests/test_auth.py
@@ -0,0 +1,73 @@
from django.test.client import RequestFactory

from nose.tools import eq_

from fjord.base.browserid import FjordVerify
from fjord.base.tests import BaseTestCase, profile, reverse, user


class TestAuth(BaseTestCase):
def test_new_user(self):
"""Tests that new users get redirected to new_user page"""
# Create a user that has no profile--this is the sign that the
# user is new!
new_user = user(save=True)
self.client_login_user(new_user)

# Now do some ridiculous setup so we can call login_success()
# on the Verify and see if it did what it should be doing.

# FIXME - this can go away post django-browserid 0.9
new_user.backend = 'django_browserid.auth.BrowserIDBackend'

get_request = RequestFactory().get(reverse('dashboard'))
get_request.user = new_user
get_request.session = self.client.session

fv = FjordVerify()
fv.user = new_user
fv.request = get_request

resp = fv.login_success()
eq_(302, resp.status_code)
eq_(resp.get('location'), reverse('new-user-view'))

def test_existing_user(self):
"""Tests that existing users get redirected to right place"""
# Create a user that has no profile--this is the sign that the
# user is new!
new_user = user(save=True)
profile(user=new_user, save=True)
self.client_login_user(new_user)

# Now do some ridiculous setup so we can call login_success()
# on the Verify and see if it did what it should be doing.

# FIXME - this can go away post django-browserid 0.9
new_user.backend = 'django_browserid.auth.BrowserIDBackend'

# First, do it RAW!
get_request = RequestFactory().get(reverse('dashboard'))
get_request.user = new_user
get_request.session = self.client.session

fv = FjordVerify()
fv.user = new_user
fv.request = get_request

resp = fv.login_success()
eq_(302, resp.status_code)
eq_(resp.get('location'), '/')

# Now do it with next!
get_request = RequestFactory().get(reverse('dashboard') + '?next=/foo')
get_request.user = new_user
get_request.session = self.client.session

fv = FjordVerify()
fv.user = new_user
fv.request = get_request

resp = fv.login_success()
eq_(302, resp.status_code)
eq_(resp.get('location'), '/foo')
1 change: 1 addition & 0 deletions fjord/base/urls.py
Expand Up @@ -8,5 +8,6 @@
url(r'^services/throw-error$', 'throw_error', name='throw-error'),

url(r'^about$', 'about_view', name='about-view'),
url(r'^new_user$', 'new_user_view', name='new-user-view'),
url(r'^robots.txt$', 'robots_view', name='robots-view')
)

0 comments on commit 99cc5ca

Please sign in to comment.