Snipt for Teams beta program.

master
Nick Sergeant 2015-07-22 14:01:27 -04:00
parent 82c3fa39e4
commit 66d0ea7a05
15 changed files with 351 additions and 18 deletions

View File

@ -0,0 +1,85 @@
# -*- 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 field 'UserProfile.teams_beta_seen'
db.add_column(u'accounts_userprofile', 'teams_beta_seen',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'UserProfile.teams_beta_seen'
db.delete_column(u'accounts_userprofile', 'teams_beta_seen')
models = {
u'accounts.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'blog_domain': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'blog_theme': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1'}),
'blog_title': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'default_editor': ('django.db.models.fields.CharField', [], {'default': "'C'", 'max_length': '250'}),
'disqus_shortname': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'editor_theme': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '250'}),
'gauges_site_id': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'gittip_username': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_client': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_height': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_slot': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_width': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_analytics_tracking_id': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'has_gravatar': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_pro': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'list_view': ('django.db.models.fields.CharField', [], {'default': "'N'", 'max_length': '1'}),
'pro_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'stripe_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'teams_beta_seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
},
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'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'}),
u'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 = ['accounts']

View File

@ -0,0 +1,86 @@
# -*- 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 field 'UserProfile.teams_beta_applied'
db.add_column(u'accounts_userprofile', 'teams_beta_applied',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'UserProfile.teams_beta_applied'
db.delete_column(u'accounts_userprofile', 'teams_beta_applied')
models = {
u'accounts.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'blog_domain': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'blog_theme': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1'}),
'blog_title': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'default_editor': ('django.db.models.fields.CharField', [], {'default': "'C'", 'max_length': '250'}),
'disqus_shortname': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'editor_theme': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '250'}),
'gauges_site_id': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'gittip_username': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_client': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_height': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_slot': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_ad_width': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'google_analytics_tracking_id': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'has_gravatar': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_pro': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'list_view': ('django.db.models.fields.CharField', [], {'default': "'N'", 'max_length': '1'}),
'pro_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'stripe_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'teams_beta_applied': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'teams_beta_seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
},
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'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'}),
u'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 = ['accounts']

View File

@ -44,6 +44,8 @@ class UserProfile(models.Model):
# User
user = models.ForeignKey(User, unique=True)
is_pro = models.BooleanField(default=False)
teams_beta_seen = models.BooleanField(default=False)
teams_beta_applied = models.BooleanField(default=False)
pro_date = models.DateTimeField(blank=True, null=True)
stripe_id = models.CharField(max_length=100, null=True, blank=True)
has_gravatar = models.BooleanField(default=False)

File diff suppressed because one or more lines are too long

View File

@ -2222,6 +2222,24 @@ div.info {
}
}
}
body.pro-signup {
div.info {
padding: 49px 0;
}
form ul.features {
margin: 20px auto 0 auto;
padding: 0;
width: 328px;
li {
font-size: 16px;
font-weight: normal;
line-height: 22px;
margin: 10px 20px;
text-align: left;
}
}
}
body.pro-signup-complete {
div.info {
margin-bottom: 0;

View File

@ -50,7 +50,7 @@
<section class="snipts" id="snipts">
{% if not request.user.is_authenticated %}
{% include 'ad-leaderboard.html' %}
{% elif not request.user.profile.is_pro %}
{% elif not request.user.profile.teams_beta_seen %}
{% include 'ad-leaderboard-pro.html' %}
{% endif %}
{% with 'true' as detail %}

View File

@ -18,7 +18,7 @@
{% endif %}>
{% if not request.user.is_authenticated %}
{% include 'ad-leaderboard.html' %}
{% elif not request.user.profile.is_pro %}
{% elif not request.user.profile.teams_beta_seen %}
{% include 'ad-leaderboard-pro.html' %}
{% endif %}
{% autopaginate snipts 10 %}

View File

@ -1,4 +1,4 @@
<a href="/pro/" class="snipt-promo">
<button class="btn btn-success btn-large pull-right">Go Pro &raquo;</button>
Go <span class="pro">Pro</span> today for only five bucks a month.
<a href="/for-teams/" class="snipt-promo">
<button class="btn btn-success btn-large pull-right">More info &raquo;</button>
Announcing Snipt for Teams! <br />Starting at just $49/month.
</a>

View File

@ -41,7 +41,7 @@
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/highlightjs-themes/tomorrow.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/blog-themes/default/style.css" />
{% else %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/snipt.css?113" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/snipt.css?114" />
{% endif %}
{% if has_snipts and not detail %}
@ -104,6 +104,9 @@
<li>
<a href="/public/" {% if '/public/' in request.path or public %} class="active"{% endif %}>Public snipts</a>
</li>
<li>
<a href="/for-teams/" {% if '/for-teams/' in request.path %} class="active"{% endif %}>Teams</a>
</li>
{% block add-snipt %}{% endblock %}
{% endif %}
</ul>
@ -151,6 +154,12 @@
</a>
</li>
{% endif %}
<li>
<a href="/for-teams/">
<i class="icon-star-empty icon-white"></i>
For Teams
</a>
</li>
{% if request.user.is_superuser %}
<li>
<a href="/admin/">
@ -208,6 +217,9 @@
<a href="/pro/"><span>Go Pro</span></a>
</li>
{% endif %}
<li class="pro">
<a href="/for-teams/"><span>For Teams</span></a>
</li>
<li class="blog">
<a href="https://blog.snipt.net/"><span>Snipt Blog</span></a>
</li>

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block page-title %}Sign up for Snipt Pro{% endblock %}
{% block body-class %}{{ block.super }} static pro signup pro-signup pro-signup-complete{% endblock %}
{% block breadcrumb %}
<li><a href="/for-teams/">For Teams</a></li>
<li><span class="prompt">/</span> <a href="#">Complete</a></li>
{% endblock %}
{% block content %}
<form class="form-horizontal static-box" id="pro-signup" method="post" action="/pro/complete/">
<fieldset>
<div class="info">
You rock.
<p class="sub" style="padding: 0 120px;">
We'll email you to arrange the beta program payment of $49, and help you get your first team created!<br /><br />
If you ever need anything at all, <a href="mailto:support@snipt.net">email support</a>, or use the chat in the bottom right.
</p>
</div>
</fieldset>
</form>
{% endblock %}
{% block analytics %}
{% if not debug %}
window.ll('tagScreen', 'Pro signup complete view');
{% endif %}
{% endblock %}

61
templates/for-teams.html Normal file
View File

@ -0,0 +1,61 @@
{% extends "base.html" %}
{% block page-title %}Request beta access to Snipt for Teams{% endblock %}
{% block body-class %}{{ block.super }} static signup pro pro-signup{% endblock %}
{% block breadcrumb %}
<li><a href="/for-teams/">Snipt for Teams</a></li>
{% endblock %}
{% block content %}
{% if request.user.profile.teams_beta_applied %}
<div class="alert alert-success" style="margin: 30px;">
You've successfully applied for the beta program - we'll email you as soon as we're ready!
</div>
{% else %}
<form class="form-horizontal static-box" id="pro-signup" method="post" action="/for-teams/complete/">
<fieldset>
<div class="info">
Snipt for Teams private beta!
<ul class="features">
<li>Team profile at snipt.net/{team-name}.</li>
<li>Members can create public or private snipts on a team.</li>
<li>Public team posts are public to the world, as they are now.</li>
<li>Private team posts are editable by all team members.</li>
<li>All team members are automatically granted personal <span class="pro">Pro</span> accounts.</li>
<li>Beta period is a one-time fee of $49, then plans starting at $49/month.</li>
</ul>
</div>
<div class="control-group">
<label class="control-label" for="name">Team name:</label>
<div class="controls">
<input required type="text" class="input-xlarge" name="name" id="name" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="members">How many users?</label>
<div class="controls">
<input required type="number" class="input-medium" name="members" id="members" />
</div>
</div>
<div class="control-group">
<label style="float: none; width: auto; text-align: left; padding-left: 180px; margin-bottom: 10px;" class="control-label" for="members">Tell us a bit about your organization and how<br />you'd like to use Snipt for your team:</label>
<div class="controls">
<textarea required name="info" name="info" id="info" class="input-xlarge" style="height: 100px;"></textarea>
</div>
</div>
<div class="form-actions">
{% csrf_token %}
<button type="submit" class="btn btn-success">Request beta access &raquo;</button>
</div>
</fieldset>
</form>
{% endif %}
{% endblock %}
{% block analytics %}
{% if not debug %}
window.ll('tagScreen', 'Team beta signup view');
{% endif %}
{% endblock %}

View File

@ -17,7 +17,7 @@
<p class="sub">
You're now a Snipt Pro.<br /><br />
You should <a target="blank" href="http://twitter.com/intent/tweet?text=I'm now a @Snipt Pro. Are you? https://snipt.net/pro/">tell the world</a>.
If you ever need anything at all, <a href="mailto:nick@snipt.net">email Nick directly</a>, or use the support link in the bottom right.
If you ever need anything at all, <a href="mailto:support@snipt.net">email support</a>, or use the chat in the bottom right.
</p>
</div>
</fieldset>

View File

@ -113,7 +113,7 @@
</div>
<div class="form-actions">
{% csrf_token %}
<button type="submit" class="btn btn-success">Subscribe</button>
<button type="submit" class="btn btn-success">Subscribe &raquo;</button>
<div class="security">
<a href="https://stripe.com/help/security">Secure</a> by default. Every Snipt page is secure.
</div>

View File

@ -13,7 +13,7 @@ from tastypie.api import Api
from utils.views import SniptRegistrationView
from jobs.views import jobs, jobs_json
from views import (homepage, lexers, login_redirect, pro, sitemap, tags,
pro_complete, user_api_key)
pro_complete, user_api_key, for_teams, for_teams_complete)
import admin as custom_admin
import os
@ -50,6 +50,9 @@ urlpatterns = patterns('',
url(r'^pro/$', pro),
url(r'^pro/complete/$', pro_complete),
url(r'^for-teams/$', for_teams),
url(r'^for-teams/complete/$', for_teams_complete),
url(r'^account/', include('accounts.urls')),
url(r'^api/public/lexer/$', lexers),

View File

@ -12,21 +12,47 @@ from django.contrib.auth.models import User
from django.db.models import Count
from snipts.models import Snipt
from taggit.models import Tag
from django.core.mail import send_mail
import datetime
import hashlib
import os
import stripe
@ajax_request
def user_api_key(request):
@render_to('for-teams.html')
def for_teams(request):
if request.user.is_authenticated():
profile = request.user.profile
profile.teams_beta_seen = True
profile.save()
return {}
if not request.user.is_authenticated():
return HttpResponseBadRequest()
@login_required
@render_to('for-teams-complete.html')
def for_teams_complete(request):
return {
'api_key': request.user.api_key.key
}
if request.method == 'POST':
name = request.POST['name']
members = request.POST['members']
info = request.POST['info']
send_mail('[Snipt] New Snipt for Teams beta request.', """
User: %s (%s)
Team name: %s
Team members: %s
Info:
%s
""" % (request.user.username, request.user.email, name, members, info), 'support@snipt.net',
['nick@nicksergeant.com'], fail_silently=False)
profile = request.user.profile
profile.teams_beta_applied = True
profile.save()
return {}
else:
return HttpResponseBadRequest()
@render_to('homepage.html')
def homepage(request):
@ -156,3 +182,13 @@ def tags(request):
'all_tags': all_tags,
'tags': popular_tags
}
@ajax_request
def user_api_key(request):
if not request.user.is_authenticated():
return HttpResponseBadRequest()
return {
'api_key': request.user.api_key.key
}