@@ -2,8 +2,10 @@ from django.contrib import admin | |||
from accounts.models import UserProfile | |||
class UserProfileAdmin(admin.ModelAdmin): | |||
list_display = ('user', 'is_pro', 'stripe_id', 'gittip_username', 'teams_beta_seen') | |||
list_display = ('user', 'is_pro', 'stripe_id', 'gittip_username', | |||
'teams_beta_seen') | |||
list_filter = ['teams_beta_seen', 'teams_beta_applied'] | |||
search_fields = ('user__username', 'gittip_username',) | |||
@@ -1,9 +1,9 @@ | |||
from django.contrib.auth.models import User | |||
from datetime import date | |||
from datetime import datetime | |||
from django.db import models | |||
from snipts.models import Snipt | |||
class UserProfile(models.Model): | |||
LIST_VIEW_CHOICES = ( | |||
@@ -42,38 +42,45 @@ class UserProfile(models.Model): | |||
) | |||
# User | |||
user = models.OneToOneField(User) | |||
is_pro = models.BooleanField(default=False) | |||
user = models.OneToOneField(User) | |||
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) | |||
list_view = models.CharField(max_length=1, null=False, blank=False,default='N', choices=LIST_VIEW_CHOICES) | |||
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) | |||
list_view = models.CharField(max_length=1, null=False, blank=False, | |||
default='N', choices=LIST_VIEW_CHOICES) | |||
# Blog | |||
blog_title = models.CharField(max_length=250, null=True, blank=True) | |||
blog_theme = models.CharField(max_length=1, null=False, blank=False, default='A', choices=THEME_CHOICES) | |||
blog_domain = models.CharField(max_length=250, null=True, blank=True) | |||
blog_title = models.CharField(max_length=250, null=True, blank=True) | |||
blog_theme = models.CharField(max_length=1, null=False, blank=False, | |||
default='A', choices=THEME_CHOICES) | |||
blog_domain = models.CharField(max_length=250, null=True, blank=True) | |||
# Editor | |||
default_editor = models.CharField(max_length=250, null=False, blank=False, default='C', choices=EDITOR_CHOICES) | |||
editor_theme = models.CharField(max_length=250, null=False, blank=False, default='default', choices=EDITOR_THEME_CHOICES) | |||
default_editor = models.CharField(max_length=250, null=False, blank=False, | |||
default='C', choices=EDITOR_CHOICES) | |||
editor_theme = models.CharField(max_length=250, null=False, blank=False, | |||
default='default', | |||
choices=EDITOR_THEME_CHOICES) | |||
# Services and Analytics | |||
gittip_username = models.CharField(max_length=250, null=True, blank=True) | |||
gittip_username = models.CharField(max_length=250, null=True, blank=True) | |||
disqus_shortname = models.CharField(max_length=250, null=True, blank=True) | |||
google_analytics_tracking_id = models.CharField(max_length=250, null=True, blank=True) | |||
gauges_site_id = models.CharField(max_length=250, null=True, blank=True) | |||
google_analytics_tracking_id = models.CharField(max_length=250, null=True, | |||
blank=True) | |||
gauges_site_id = models.CharField(max_length=250, null=True, blank=True) | |||
# Google Ads | |||
google_ad_client = models.CharField(max_length=250, null=True, blank=True) | |||
google_ad_slot = models.CharField(max_length=250, null=True, blank=True) | |||
google_ad_width = models.CharField(max_length=250, null=True, blank=True) | |||
google_ad_slot = models.CharField(max_length=250, null=True, blank=True) | |||
google_ad_width = models.CharField(max_length=250, null=True, blank=True) | |||
google_ad_height = models.CharField(max_length=250, null=True, blank=True) | |||
def get_blog_posts(self): | |||
return Snipt.objects.filter(user=self.user, blog_post=True, public=True) | |||
return Snipt.objects.filter(user=self.user, blog_post=True, | |||
public=True) | |||
def get_primary_blog_domain(self): | |||
if not self.blog_domain: | |||
@@ -98,10 +105,14 @@ class UserProfile(models.Model): | |||
return url | |||
def has_public_snipts(self): | |||
return True if Snipt.objects.filter(user=self, public=True).count() > 0 else False | |||
return True \ | |||
if Snipt.objects.filter(user=self, | |||
public=True).count() > 0 \ | |||
else False | |||
def get_account_age(self): | |||
delta = datetime.now().replace(tzinfo=None) - self.user.date_joined.replace(tzinfo=None) | |||
delta = datetime.now().replace(tzinfo=None) - \ | |||
self.user.date_joined.replace(tzinfo=None) | |||
return delta.days | |||
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0]) |
@@ -2,9 +2,11 @@ from django.conf.urls import * | |||
from accounts import views | |||
urlpatterns = patterns('', | |||
url(r'^stats/$', views.stats, name='account-stats'), | |||
url(r'^cancel-subscription/$', views.cancel_subscription, name='cancel-subscription'), | |||
url(r'^stripe-account-details/$', views.stripe_account_details, name='stripe-account-details'), | |||
url(r'^', views.account, name='account-detail'), | |||
) | |||
urlpatterns = \ | |||
patterns('', | |||
url(r'^stats/$', views.stats, name='account-stats'), | |||
url(r'^cancel-subscription/$', views.cancel_subscription, | |||
name='cancel-subscription'), | |||
url(r'^stripe-account-details/$', views.stripe_account_details, | |||
name='stripe-account-details'), | |||
url(r'^', views.account, name='account-detail')) |
@@ -3,7 +3,8 @@ from django.contrib.auth.decorators import login_required | |||
from annoying.decorators import ajax_request, render_to | |||
from snipts.models import Snipt | |||
import os, stripe | |||
import os | |||
import stripe | |||
@login_required | |||
@@ -19,7 +20,8 @@ def cancel_subscription(request): | |||
if request.user.profile.stripe_id is None: | |||
return {} | |||
else: | |||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', settings.STRIPE_SECRET_KEY) | |||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', | |||
settings.STRIPE_SECRET_KEY) | |||
customer = stripe.Customer.retrieve(request.user.profile.stripe_id) | |||
customer.delete() | |||
@@ -28,7 +30,8 @@ def cancel_subscription(request): | |||
profile.stripe_id = None | |||
profile.save() | |||
return { 'deleted': True } | |||
return {'deleted': True} | |||
@login_required | |||
@ajax_request | |||
@@ -37,7 +40,8 @@ def stripe_account_details(request): | |||
if request.user.profile.stripe_id is None: | |||
return {} | |||
else: | |||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', settings.STRIPE_SECRET_KEY) | |||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', | |||
settings.STRIPE_SECRET_KEY) | |||
customer = stripe.Customer.retrieve(request.user.profile.stripe_id) | |||
data = { | |||
@@ -2,9 +2,11 @@ from django.contrib.auth.admin import UserAdmin | |||
from django.contrib.auth.models import User | |||
from django.contrib import admin | |||
class UserAdmin(UserAdmin): | |||
list_display = ['username', 'email', 'first_name', 'last_name', 'last_login', | |||
'date_joined', 'is_active', 'is_staff', 'api_key'] | |||
list_display = ['username', 'email', 'first_name', 'last_name', | |||
'last_login', 'date_joined', 'is_active', 'is_staff', | |||
'api_key'] | |||
list_filter = ['is_staff', 'is_superuser', 'is_active'] | |||
ordering = ['-date_joined'] | |||
@@ -12,19 +12,26 @@ class BlogMiddleware: | |||
host = request.META.get('HTTP_HOST', '') | |||
host_s = host.replace('www.', '').split('.') | |||
if host != 'snipt.net' and host != 'snipt.localhost' and host != 'local.snipt.net': | |||
if host != 'snipt.net' and \ | |||
host != 'snipt.localhost' and \ | |||
host != 'local.snipt.net': | |||
if len(host_s) > 2: | |||
if host_s[1] == 'snipt': | |||
blog_user = ''.join(host_s[:-2]) | |||
if '-' in blog_user: | |||
request.blog_user = get_object_or_None(User, username__iexact=blog_user) | |||
request.blog_user = \ | |||
get_object_or_None(User, | |||
username__iexact=blog_user) | |||
if request.blog_user is None: | |||
request.blog_user = get_object_or_404(User, username__iexact=blog_user.replace('-', '_')) | |||
request.blog_user = \ | |||
get_object_or_404(User, | |||
username__iexact=blog_user.replace('-', '_')) | |||
else: | |||
request.blog_user = get_object_or_404(User, username__iexact=blog_user) | |||
request.blog_user = \ | |||
get_object_or_404(User, username__iexact=blog_user) | |||
if request.blog_user is None: | |||
pro_users = User.objects.filter(userprofile__is_pro=True) | |||
@@ -1,5 +1,3 @@ | |||
from django.conf.urls import * | |||
urlpatterns = patterns('', | |||
url(r'^$', views.blog, name='blog'), | |||
) | |||
urlpatterns = patterns('', url(r'^$', views.blog, name='blog')) |
@@ -11,6 +11,7 @@ THEME_CHOICES = { | |||
'A': 'blogs/themes/pro-adams/', | |||
} | |||
def blog_list(request, username_or_custom_slug=None): | |||
if username_or_custom_slug: | |||
@@ -19,17 +20,25 @@ def blog_list(request, username_or_custom_slug=None): | |||
snipts = Snipt.objects.filter(user=request.blog_user, | |||
blog_post=True, | |||
public=True, | |||
publish_date__lte=datetime.datetime.now() | |||
).order_by('-publish_date').exclude(title__iexact='Homepage').exclude(title__iexact='Work') | |||
normal_snipts = Snipt.objects.filter(blog_post=False, user=request.blog_user, public=True).order_by('-created') | |||
publish_date__lte=datetime.datetime.now()) \ | |||
.order_by('-publish_date') \ | |||
.exclude(title__iexact='Homepage') \ | |||
.exclude(title__iexact='Work') | |||
normal_snipts = Snipt.objects.filter(blog_post=False, | |||
user=request.blog_user, | |||
public=True) \ | |||
.order_by('-created') | |||
normal_snipts = normal_snipts.exclude(title__in=['']) | |||
normal_snipts = normal_snipts.exclude(tags__name__in=['tmp']) | |||
normal_snipts = normal_snipts[:3] | |||
sidebar = get_object_or_None(Snipt, user=request.blog_user, title='Sidebar', blog_post=True) | |||
header = get_object_or_None(Snipt, user=request.blog_user, title='Header', blog_post=True) | |||
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', lexer='css', blog_post=True) | |||
sidebar = get_object_or_None(Snipt, user=request.blog_user, | |||
title='Sidebar', blog_post=True) | |||
header = get_object_or_None(Snipt, user=request.blog_user, title='Header', | |||
blog_post=True) | |||
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', | |||
lexer='css', blog_post=True) | |||
context = { | |||
'blog_user': request.blog_user, | |||
@@ -55,26 +64,33 @@ def blog_list(request, username_or_custom_slug=None): | |||
context, | |||
context_instance=RequestContext(request)) | |||
def blog_post(request, username_or_custom_slug): | |||
snipt = get_object_or_404(Snipt, user=request.blog_user, | |||
blog_post=True, | |||
public=True, | |||
publish_date__lte=datetime.datetime.now(), | |||
slug=username_or_custom_slug, | |||
) | |||
blog_post=True, | |||
public=True, | |||
publish_date__lte=datetime.datetime.now(), | |||
slug=username_or_custom_slug) | |||
snipts = Snipt.objects.filter(user=request.blog_user, | |||
blog_post=True, | |||
public=True, | |||
publish_date__lte=datetime.datetime.now() | |||
).order_by('-publish_date').exclude(title__iexact='Homepage').exclude(title__iexact='Work') | |||
sidebar = get_object_or_None(Snipt, user=request.blog_user, title='Sidebar', blog_post=True) | |||
header = get_object_or_None(Snipt, user=request.blog_user, title='Header', blog_post=True) | |||
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', lexer='css', blog_post=True) | |||
normal_snipts = Snipt.objects.filter(blog_post=False, user=request.blog_user, public=True).order_by('-created') | |||
publish_date__lte=datetime.datetime.now()) \ | |||
.order_by('-publish_date') \ | |||
.exclude(title__iexact='Homepage') \ | |||
.exclude(title__iexact='Work') | |||
sidebar = get_object_or_None(Snipt, user=request.blog_user, | |||
title='Sidebar', blog_post=True) | |||
header = get_object_or_None(Snipt, user=request.blog_user, title='Header', | |||
blog_post=True) | |||
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', | |||
lexer='css', blog_post=True) | |||
normal_snipts = Snipt.objects.filter(blog_post=False, | |||
user=request.blog_user, | |||
public=True).order_by('-created') | |||
normal_snipts = normal_snipts.exclude(title__in=['']) | |||
normal_snipts = normal_snipts.exclude(tags__name__in=['tmp']) | |||
normal_snipts = normal_snipts[:3] | |||
@@ -100,16 +116,13 @@ def blog_post(request, username_or_custom_slug): | |||
template = '{}/post.html'.format(template) | |||
return render_to_response( | |||
template, | |||
context, | |||
context_instance=RequestContext(request) | |||
) | |||
return render_to_response(template, | |||
context, | |||
context_instance=RequestContext(request)) | |||
def rss(request, context): | |||
return render_to_response( | |||
'blogs/themes/default/rss.xml', | |||
context, | |||
context_instance=RequestContext(request), | |||
content_type="application/rss+xml" | |||
) | |||
return render_to_response('blogs/themes/default/rss.xml', | |||
context, | |||
context_instance=RequestContext(request), | |||
content_type="application/rss+xml") |
@@ -1,9 +0,0 @@ | |||
from django.contrib import admin | |||
from jobs.models import Job | |||
class JobAdmin(admin.ModelAdmin): | |||
list_display = ('title', 'created', 'company', 'url',) | |||
ordering = ('-created',) | |||
admin.site.register(Job, JobAdmin) |
@@ -1,48 +0,0 @@ | |||
from django.conf import settings | |||
from django.core.management.base import BaseCommand | |||
from jobs.models import Job | |||
import json | |||
import requests | |||
class Command(BaseCommand): | |||
help = 'Import jobs' | |||
listings = [] | |||
def get_for_page(self, page): | |||
r = requests.get('{}&page={}'.format(settings.JOBS_BASE_URL, page)) | |||
obj = r.json() | |||
self.listings.extend(obj['listings']['listing']) | |||
if page < obj['listings']['pages']: | |||
return self.get_for_page(page + 1) | |||
return self.listings | |||
def handle(self, *args, **options): | |||
listings = self.get_for_page(1) | |||
jobs = Job.objects.all() | |||
for job in jobs: | |||
job.delete() | |||
for listing in listings: | |||
try: | |||
location = listing['company']['location']['city'] | |||
except: | |||
location = '' | |||
try: | |||
company = str(listing['company']['name']) | |||
except: | |||
company = '' | |||
newjob = Job(title=listing['title'], | |||
company=company, | |||
location=location, | |||
url=listing['url'], | |||
data=json.dumps(listing), | |||
created=listing['post_date']) | |||
newjob.save() |
@@ -1,25 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
from __future__ import unicode_literals | |||
from django.db import models, migrations | |||
class Migration(migrations.Migration): | |||
dependencies = [ | |||
] | |||
operations = [ | |||
migrations.CreateModel( | |||
name='Job', | |||
fields=[ | |||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |||
('title', models.CharField(max_length=255)), | |||
('company', models.CharField(max_length=255)), | |||
('location', models.CharField(max_length=255)), | |||
('url', models.CharField(max_length=255)), | |||
('data', models.TextField(null=True, blank=True)), | |||
('created', models.DateTimeField()), | |||
], | |||
), | |||
] |
@@ -1,16 +0,0 @@ | |||
from django.db import models | |||
class Job(models.Model): | |||
"""An individual Job.""" | |||
title = models.CharField(max_length=255) | |||
company = models.CharField(max_length=255) | |||
location = models.CharField(max_length=255) | |||
url = models.CharField(max_length=255) | |||
data = models.TextField(blank=True, null=True) | |||
created = models.DateTimeField() | |||
def __unicode__(self): | |||
return self.title |
@@ -1,70 +0,0 @@ | |||
{% extends "base.html" %} | |||
{% load humanize %} | |||
{% load pagination_tags %} | |||
{% block page-title %}Snipt Jobs{% endblock %} | |||
{% block body-class %}{{ block.super }} static jobs search{% endblock %} | |||
{% block js %} | |||
window.jobs = [ | |||
{% for job in jobs %} | |||
{{ job.data|safe }}{% if not forloop.last %},{% endif %} | |||
{% endfor %} | |||
]; | |||
{% endblock %} | |||
{% block breadcrumb %} | |||
<li><a href="/jobs/">Snipt Jobs</a></li> | |||
{% endblock %} | |||
{% block content %} | |||
<div ng-controller="JobSearchController" ng-cloak class="ng-cloak"> | |||
<div class="static-box {% if page.object_list|length > 0 %}has-snipts{% endif %}"> | |||
<form method="get" class="form-search" action="."> | |||
<input ng-model="query" type="text" class="search-query" name="q" placeholder="Filter jobs" /> | |||
</form> | |||
</div> | |||
<div class="loading-jobs" ng-show="!jobs"> | |||
Loading jobs… | |||
</div> | |||
<div class="pagination ng-cloak" ng-cloak ng-show="jobs"> | |||
<button class="btn prev" style="margin: 0 10px;" href ng-disabled="currentPage == 0" ng-click="currentPage=currentPage - 1">Previous</button> | |||
<span class="page">{[{ currentPage + 1 }]} / {[{ numberOfPages() }]}</span> | |||
<button class="btn next" style="margin: 0 10px;" ng-disabled="currentPage >= filteredJobs.length / pageSize - 1" ng-click="currentPage=currentPage + 1">Next</button> | |||
</div> | |||
<section class="jobs"> | |||
<ul> | |||
<li ng-repeat="job in filteredJobs|startFrom:currentPage * pageSize|limitTo:pageSize"> | |||
<a href="{[{ job.url }]}?aff=dbe7e" class="group job-link"> | |||
<img ng-show="job.company.logo" ng-src="{[{ job.company.logo }]}" /> | |||
<span class="left"> | |||
<span class="job">{[{ job.title }]}</span> | |||
<span class="company">{[{ job.company.name }]}</span> | |||
</span> | |||
<span class="right"> | |||
<span class="location">{[{ job.company.location.city }]}</span> | |||
<span class="date">{[{ job.created }]}</span> | |||
</span> | |||
</a> | |||
</li> | |||
</ul> | |||
</section> | |||
<div class="pagination ng-cloak" ng-cloak ng-show="jobs"> | |||
<button class="btn prev" style="margin: 0 10px;" href ng-disabled="currentPage == 0" ng-click="currentPage=currentPage - 1">Previous</button> | |||
<span class="page">{[{ currentPage + 1 }]} / {[{ numberOfPages() }]}</span> | |||
<button class="btn next" style="margin: 0 10px;" ng-disabled="currentPage >= filteredJobs.length / pageSize - 1" ng-click="currentPage=currentPage + 1">Next</button> | |||
</div> | |||
</div> | |||
{% endblock %} | |||
{% block ad %}{% endblock %} | |||
{% block aside-top %} | |||
<div class="post-job"> | |||
<a id="post-job" href="http://www.authenticjobs.com/post/?aff=dbe7e" class="btn btn-large btn-success">Post a job</a> | |||
<p> | |||
We use <a class="job-link" href="http://www.authenticjobs.com?aff=dbe7e">Authentic Jobs</a> for our job board provider. Once you post your ad, it will appear on Snipt within 30 minutes. | |||
</p> | |||
</div> | |||
{% endblock %} |
@@ -1,16 +0,0 @@ | |||
""" | |||
This file demonstrates writing tests using the unittest module. These will pass | |||
when you run "manage.py test". | |||
Replace this with more appropriate tests for your application. | |||
""" | |||
from django.test import TestCase | |||
class SimpleTest(TestCase): | |||
def test_basic_addition(self): | |||
""" | |||
Tests that 1 + 1 always equals 2. | |||
""" | |||
self.assertEqual(1 + 1, 2) |
@@ -1,22 +0,0 @@ | |||
from annoying.decorators import ajax_request, render_to | |||
from jobs.models import Job | |||
import json | |||
@render_to('jobs/jobs.html') | |||
def jobs(request): | |||
return {} | |||
@ajax_request | |||
def jobs_json(request): | |||
jobs_json = [] | |||
jobs = Job.objects.all().order_by('-created') | |||
for job in jobs: | |||
jobs_json.append(json.loads(job.data)) | |||
return jobs_json |
@@ -1,13 +1,15 @@ | |||
import dj_database_url, os | |||
import dj_database_url | |||
import os | |||
from urlparse import urlparse | |||
if 'DATABASE_URL' in os.environ: | |||
DATABASES = { 'default': dj_database_url.config() } | |||
DATABASES = {'default': dj_database_url.config()} | |||
es = urlparse(os.environ.get('SEARCHBOX_SSL_URL') or 'http://127.0.0.1:9200/') | |||
es = urlparse(os.environ.get('SEARCHBOX_SSL_URL') or | |||
'http://127.0.0.1:9200/') | |||
port = es.port or 80 | |||
HAYSTACK_CONNECTIONS = { | |||
@@ -19,9 +21,11 @@ if 'DATABASE_URL' in os.environ: | |||
} | |||
if es.username: | |||
HAYSTACK_CONNECTIONS['default']['KWARGS'] = {"http_auth": es.username + ':' + es.password} | |||
HAYSTACK_CONNECTIONS['default']['KWARGS'] = { | |||
"http_auth": es.username + ':' + es.password | |||
} | |||
ABSOLUTE_URL_OVERRIDES = { 'auth.user': lambda u: "/%s/" % u.username, } | |||
ABSOLUTE_URL_OVERRIDES = {'auth.user': lambda u: "/%s/" % u.username} | |||
ACCOUNT_ACTIVATION_DAYS = 0 | |||
ADMINS = (('Nick Sergeant', 'nick@snipt.net'),) | |||
ALLOWED_HOSTS = ['*'] | |||
@@ -46,7 +50,7 @@ MEDIA_URL = '/media/uploads/' | |||
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' | |||
POSTMARK_API_KEY = os.environ.get('POSTMARK_API_KEY', '') | |||
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) | |||
RAVEN_CONFIG = { 'dsn': os.environ.get('RAVEN_CONFIG_DSN', '') } | |||
RAVEN_CONFIG = {'dsn': os.environ.get('RAVEN_CONFIG_DSN', '')} | |||
REGISTRATION_EMAIL_HTML = False | |||
ROOT_URLCONF = 'urls' | |||
SECRET_KEY = os.environ.get('SECRET_KEY', 'changeme') | |||
@@ -57,11 +61,14 @@ SESSION_COOKIE_AGE = 15801100 | |||
SESSION_COOKIE_SECURE = True if 'USE_SSL' in os.environ else False | |||
SITE_ID = 1 | |||
STATICFILES_DIRS = (os.path.join(BASE_PATH, 'media'),) | |||
STATICFILES_FINDERS = ('django.contrib.staticfiles.finders.FileSystemFinder','django.contrib.staticfiles.finders.AppDirectoriesFinder',) | |||
STATICFILES_FINDERS = ('django.contrib.staticfiles.finders.FileSystemFinder', | |||
'django.contrib.staticfiles.finders.AppDirectoriesFinder') | |||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' | |||
STATIC_ROOT = os.path.join(BASE_PATH, 'static') | |||
STATIC_URL = '/static/' | |||
TASTYPIE_CANNED_ERROR = "There was an error with your request. The site developers have a record of this error, please email api@snipt.net and we'll help you out." | |||
TASTYPIE_CANNED_ERROR = """There was an error with your request. The site | |||
developers have a record of this error, please email api@snipt.net and | |||
we'll help you out.""" | |||
TEMPLATE_DIRS = (os.path.join(PROJECT_PATH, 'templates'),) | |||
TEMPLATE_DEBUG = DEBUG | |||
TIME_ZONE = 'America/New_York' | |||
@@ -93,7 +100,6 @@ INSTALLED_APPS = ( | |||
'typogrify', | |||
'accounts', | |||
'blogs', | |||
'jobs', | |||
'snipts', | |||
'utils', | |||
) | |||
@@ -2,7 +2,7 @@ CSRF_COOKIE_SECURE = False | |||
DEBUG = True | |||
INTERCOM_SECRET_KEY = '' | |||
POSTMARK_API_KEY = '' | |||
RAVEN_CONFIG = { 'dsn': '' } | |||
RAVEN_CONFIG = {'dsn': ''} | |||
SECRET_KEY = 'changeme' | |||
SESSION_COOKIE_SECURE = False | |||
SSLIFY_DISABLE = False | |||
@@ -5,7 +5,9 @@ from snipts.models import Favorite, Snipt | |||
class SniptAdmin(admin.ModelAdmin): | |||
readonly_fields = ('user',) | |||
list_display = ('title', 'slug', 'views', 'favs', 'user', 'lexer', 'public', 'blog_post', 'created', 'modified', 'publish_date',) | |||
list_display = ('title', 'slug', 'views', 'favs', 'user', 'lexer', | |||
'public', 'blog_post', 'created', 'modified', | |||
'publish_date') | |||
list_filter = ('blog_post',) | |||
search_fields = ('title', 'slug', 'user__username', 'lexer', 'id', 'key',) | |||
ordering = ('-created',) | |||
@@ -13,6 +15,7 @@ class SniptAdmin(admin.ModelAdmin): | |||
admin.site.register(Snipt, SniptAdmin) | |||
class FavoriteAdmin(admin.ModelAdmin): | |||
readonly_fields = ('snipt', 'user',) | |||
list_display = ('snipt', 'user', 'created',) | |||
@@ -12,12 +12,14 @@ from haystack.query import SearchQuerySet | |||
from accounts.models import UserProfile | |||
from tastypie.cache import SimpleCache | |||
from tastypie.fields import ListField | |||
from django.http import HttpResponse | |||
from taggit.models import Tag | |||
from django.db import models | |||
from tastypie import fields | |||
import datetime, hashlib, time, re | |||
import datetime | |||
import hashlib | |||
import re | |||
import time | |||
import parsedatetime as pdt | |||
@@ -49,6 +51,7 @@ class PrivateFavoriteAuthorization(Authorization): | |||
def delete_detail(self, object_list, bundle): | |||
return bundle.obj.user == bundle.request.user | |||
class PrivateSniptAuthorization(Authorization): | |||
def read_list(self, object_list, bundle): | |||
return object_list.filter(user=bundle.request.user) | |||
@@ -74,6 +77,7 @@ class PrivateSniptAuthorization(Authorization): | |||
def delete_detail(self, object_list, bundle): | |||
return bundle.obj.user == bundle.request.user | |||
class PrivateUserProfileAuthorization(Authorization): | |||
def read_list(self, object_list, bundle): | |||
raise Unauthorized() | |||
@@ -99,6 +103,7 @@ class PrivateUserProfileAuthorization(Authorization): | |||
def delete_detail(self, object_list, bundle): | |||
raise Unauthorized() | |||
class PrivateUserAuthorization(Authorization): | |||
def read_list(self, object_list, bundle): | |||
raise Unauthorized() | |||
@@ -130,22 +135,27 @@ class FavoriteValidation(Validation): | |||
errors = {} | |||
snipt = bundle.data['snipt'] | |||
if Favorite.objects.filter(user=bundle.request.user, snipt=snipt).count(): | |||
if Favorite.objects.filter(user=bundle.request.user, | |||
snipt=snipt).count(): | |||
errors['duplicate'] = 'User has already favorited this snipt.' | |||
return errors | |||
class SniptValidation(Validation): | |||
def is_valid(self, bundle, request=None): | |||
errors = {} | |||
if 'pk' not in bundle.data and \ | |||
request.user.profile.get_account_age() > 7 and \ | |||
request.user.profile.is_pro == False: | |||
errors['expired'] = "Your trial has expired. You'll need to go Pro (https://snipt.net/pro/) in order to create new snipts." | |||
request.user.profile.get_account_age() > 7 and \ | |||
request.user.profile.is_pro is False: | |||
errors['expired'] = """Your trial has expired. You'll need | |||
to go Pro (https://snipt.net/pro/) | |||
in order to create new snipts.""" | |||
return errors | |||
class UserProfileValidation(Validation): | |||
def is_valid(self, bundle, request=None): | |||
errors = {} | |||
@@ -153,7 +163,9 @@ class UserProfileValidation(Validation): | |||
for field in bundle.data: | |||
if bundle.data[field]: | |||
if not re.match('^[ A-Za-z0-9\/\@\._-]*$', bundle.data[field]): | |||
errors[field] = 'Only spaces, letters, numbers, underscores, dashes, periods, forward slashes, and "at sign" are valid.' | |||
errors[field] = """Only spaces, letters, numbers, | |||
underscores, dashes, periods, forward | |||
slashes, and "at sign" are valid.""" | |||
return errors | |||
@@ -162,26 +174,31 @@ class PublicUserResource(ModelResource): | |||
class Meta: | |||
queryset = User.objects.all() | |||
resource_name = 'user' | |||
fields = ['id', 'username',] | |||
fields = ['id', 'username'] | |||
include_absolute_url = True | |||
allowed_methods = ['get'] | |||
filtering = { 'username': 'exact', } | |||
filtering = {'username': 'exact'} | |||
max_limit = 200 | |||
cache = SimpleCache() | |||
def dehydrate(self, bundle): | |||
bundle.data['snipts'] = '/api/public/snipt/?user=%d' % bundle.obj.id | |||
bundle.data['email_md5'] = hashlib.md5(bundle.obj.email.lower()).hexdigest() | |||
bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id, public=True).count() | |||
bundle.data['email_md5'] = hashlib \ | |||
.md5(bundle.obj.email.lower()) \ | |||
.hexdigest() | |||
bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id, | |||
public=True).count() | |||
return bundle | |||
class PublicTagResource(ModelResource): | |||
class Meta: | |||
queryset = Tag.objects.filter() | |||
queryset = queryset.annotate(count=models.Count('taggit_taggeditem_items__id')) | |||
queryset = queryset.annotate( | |||
count=models.Count('taggit_taggeditem_items__id')) | |||
queryset = queryset.order_by('-count', 'name') | |||
resource_name = 'tag' | |||
fields = ['id', 'name',] | |||
fields = ['id', 'name'] | |||
allowed_methods = ['get'] | |||
max_limit = 200 | |||
cache = SimpleCache() | |||
@@ -202,19 +219,22 @@ class PublicTagResource(ModelResource): | |||
bundle.data['snipts'] = '/api/public/snipt/?tag=%d' % bundle.obj.id | |||
return bundle | |||
class PublicSniptResource(ModelResource): | |||
user = fields.ForeignKey(PublicUserResource, 'user', full=True) | |||
tags = fields.ToManyField(PublicTagResource, 'tags', related_name='tag', full=True) | |||
tags = fields.ToManyField(PublicTagResource, 'tags', related_name='tag', | |||
full=True) | |||
class Meta: | |||
queryset = Snipt.objects.filter(public=True).order_by('-created') | |||
resource_name = 'snipt' | |||
fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', 'line_count', | |||
'stylized', 'created', 'modified', 'publish_date', 'blog_post', 'meta',] | |||
fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', | |||
'line_count', 'stylized', 'created', 'modified', | |||
'publish_date', 'blog_post', 'meta'] | |||
include_absolute_url = True | |||
allowed_methods = ['get'] | |||
filtering = { 'user': 'exact', 'blog_post': 'exact' } | |||
ordering = ['created', 'modified',] | |||
filtering = {'user': 'exact', 'blog_post': 'exact'} | |||
ordering = ['created', 'modified'] | |||
max_limit = 200 | |||
cache = SimpleCache() | |||
@@ -222,7 +242,8 @@ class PublicSniptResource(ModelResource): | |||
bundle.data['embed_url'] = bundle.obj.get_embed_url() | |||
bundle.data['raw_url'] = bundle.obj.get_raw_url() | |||
bundle.data['full_absolute_url'] = bundle.obj.get_full_absolute_url() | |||
bundle.data['description_rendered'] = linebreaksbr(urlize(bundle.obj.description)) | |||
bundle.data['description_rendered'] = \ | |||
linebreaksbr(urlize(bundle.obj.description)) | |||
if 'omit_code' in bundle.request.GET: | |||
del bundle.data['code'] | |||
@@ -272,8 +293,10 @@ class PrivateUserProfileResource(ModelResource): | |||
bundle.data['is_pro'] = bundle.obj.user.profile.is_pro | |||
return bundle | |||
class PrivateUserResource(ModelResource): | |||
profile = fields.ForeignKey(PrivateUserProfileResource, 'profile', full=False) | |||
profile = fields.ForeignKey(PrivateUserProfileResource, 'profile', | |||
full=False) | |||
class Meta: | |||
queryset = User.objects.all() | |||
@@ -289,18 +312,25 @@ class PrivateUserResource(ModelResource): | |||
cache = SimpleCache() | |||
def dehydrate(self, bundle): | |||
bundle.data['email_md5'] = hashlib.md5(bundle.obj.email.lower()).hexdigest() | |||
bundle.data['email_md5'] = hashlib \ | |||
.md5(bundle.obj.email.lower()) \ | |||
.hexdigest() | |||
bundle.data['is_pro'] = bundle.obj.profile.is_pro | |||
bundle.data['stats'] = { | |||
'public_snipts': Snipt.objects.filter(user=bundle.obj.id, public=True).count(), | |||
'private_snipts': Snipt.objects.filter(user=bundle.obj.id, public=False).count(), | |||
'public_snipts': Snipt.objects.filter(user=bundle.obj.id, | |||
public=True).count(), | |||
'private_snipts': Snipt.objects.filter(user=bundle.obj.id, | |||
public=False).count(), | |||
'total_snipts': Snipt.objects.filter(user=bundle.obj.id).count(), | |||
'total_views': Snipt.objects.filter(user=bundle.obj.id).aggregate(models.Sum('views'))['views__sum'] | |||
'total_views': Snipt.objects.filter(user=bundle.obj.id).aggregate( | |||
models.Sum('views'))['views__sum'] | |||
} | |||
bundle.data['lexers'] = [snipt['lexer'] for snipt in \ | |||
Snipt.objects.filter(user=bundle.obj).values('lexer').distinct()] | |||
bundle.data['lexers'] = [ | |||
snipt['lexer'] for snipt in Snipt.objects.filter(user=bundle.obj) | |||
.values('lexer').distinct()] | |||
return bundle | |||
class PrivateSniptResource(ModelResource): | |||
user = fields.ForeignKey(PrivateUserResource, 'user', full=True) | |||
tags_list = ListField() | |||
@@ -308,15 +338,16 @@ class PrivateSniptResource(ModelResource): | |||
class Meta: | |||
queryset = Snipt.objects.all().order_by('-created') | |||
resource_name = 'snipt' | |||
fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', 'line_count', 'stylized', | |||
'key', 'public', 'blog_post', 'created', 'modified', 'publish_date', 'meta',] | |||
fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', | |||
'line_count', 'stylized', 'key', 'public', 'blog_post', | |||
'created', 'modified', 'publish_date', 'meta'] | |||
include_absolute_url = True | |||
detail_allowed_methods = ['get', 'patch', 'put', 'delete'] | |||
list_allowed_methods = ['get', 'post'] | |||
authentication = ApiKeyAuthentication() | |||
authorization = PrivateSniptAuthorization() | |||
validation = SniptValidation() | |||
ordering = ['created', 'modified',] | |||
ordering = ['created', 'modified'] | |||
always_return_data = True | |||
max_limit = 200 | |||
cache = SimpleCache() | |||
@@ -326,12 +357,14 @@ class PrivateSniptResource(ModelResource): | |||
bundle.data['raw_url'] = bundle.obj.get_raw_url() | |||
bundle.data['tags_list'] = edit_string_for_tags(bundle.obj.tags.all()) | |||
bundle.data['full_absolute_url'] = bundle.obj.get_full_absolute_url() | |||
bundle.data['description_rendered'] = linebreaksbr(urlize(bundle.obj.description)) | |||
bundle.data['description_rendered'] = \ | |||
linebreaksbr(urlize(bundle.obj.description)) | |||
bundle.data['views'] = bundle.obj.views | |||
bundle.data['favs'] = bundle.obj.favs() | |||
if bundle.data['publish_date']: | |||
bundle.data['publish_date'] = date(bundle.data['publish_date'], 'M d, Y \\a\\t h:i A') | |||
bundle.data['publish_date'] = \ | |||
date(bundle.data['publish_date'], 'M d, Y \\a\\t h:i A') | |||
return bundle | |||
@@ -342,8 +375,9 @@ class PrivateSniptResource(ModelResource): | |||
if 'blog_post' in bundle.data: | |||
bundle = self._clean_publish_date(bundle) | |||
return super(PrivateSniptResource, self).obj_create(bundle, | |||
user=bundle.request.user, **kwargs) | |||
return super(PrivateSniptResource, self) \ | |||
.obj_create(bundle, | |||
user=bundle.request.user, **kwargs) | |||
def obj_update(self, bundle, **kwargs): | |||
bundle.data['user'] = bundle.request.user | |||
@@ -359,8 +393,9 @@ class PrivateSniptResource(ModelResource): | |||
if 'blog_post' in bundle.data: | |||
bundle = self._clean_publish_date(bundle) | |||
return super(PrivateSniptResource, self).obj_update(bundle, | |||
user=bundle.request.user, **kwargs) | |||
return super(PrivateSniptResource, self) \ | |||
.obj_update(bundle, | |||
user=bundle.request.user, **kwargs) | |||
def _clean_publish_date(self, bundle): | |||
if bundle.data['blog_post'] and 'publish_date' not in bundle.data: | |||
@@ -407,6 +442,7 @@ class PrivateSniptResource(ModelResource): | |||
else: | |||
bundle.obj.tags.set() | |||
class PrivateFavoriteResource(ModelResource): | |||
user = fields.ForeignKey(PrivateUserResource, 'user', full=True) | |||
snipt = fields.ForeignKey(PrivateSniptResource, 'snipt', full=False) | |||
@@ -414,24 +450,24 @@ class PrivateFavoriteResource(ModelResource): | |||
class Meta: | |||
queryset = Favorite.objects.all().order_by('-created') | |||
resource_name = 'favorite' | |||
fields = ['id',] | |||
fields = ['id'] | |||
validation = FavoriteValidation() | |||
detail_allowed_methods = ['get', 'post', 'delete'] | |||
list_allowed_methods = ['get', 'post',] | |||
list_allowed_methods = ['get', 'post'] | |||
authentication = ApiKeyAuthentication() | |||
authorization = PrivateFavoriteAuthorization() | |||
ordering = ['created',] | |||
ordering = ['created'] | |||
always_return_data = True | |||
max_limit = 200 | |||
cache = SimpleCache() | |||
def dehydrate(self, bundle): | |||
bundle.data['snipt'] = '/api/public/snipt/{}/'.format( | |||
bundle.obj.snipt.pk) | |||
bundle.obj.snipt.pk) | |||
return bundle | |||
def obj_create(self, bundle, **kwargs): | |||
bundle.data['user'] = bundle.request.user | |||
bundle.data['snipt'] = Snipt.objects.get(pk=bundle.data['snipt']) | |||
return super(PrivateFavoriteResource, self).obj_create(bundle, | |||
user=bundle.request.user, **kwargs) | |||
return super(PrivateFavoriteResource, self) \ | |||
.obj_create(bundle, user=bundle.request.user, **kwargs) |
@@ -1,6 +1,7 @@ | |||
from django.forms import ModelForm | |||
from snipts.models import Snipt | |||
class SniptForm(ModelForm): | |||
class Meta: | |||
model = Snipt |
@@ -15,36 +15,40 @@ from pygments.formatters import HtmlFormatter | |||
from snipts.utils import slugify_uniquely | |||
import datetime, hashlib, random, re | |||
import datetime | |||
import hashlib | |||
import random | |||
import re | |||
class Snipt(models.Model): | |||
"""An individual Snipt.""" | |||
user = models.ForeignKey(User, blank=True, null=True) | |||
user = models.ForeignKey(User, blank=True, null=True) | |||
title = models.CharField(max_length=255, blank=True, null=True, default='Untitled') | |||
slug = models.SlugField(max_length=255, blank=True) | |||
custom_slug = models.SlugField(max_length=255, blank=True) | |||
tags = TaggableManager() | |||
title = models.CharField(max_length=255, blank=True, null=True, | |||
default='Untitled') | |||
slug = models.SlugField(max_length=255, blank=True) | |||
custom_slug = models.SlugField(max_length=255, blank=True) | |||
tags = TaggableManager() | |||
lexer = models.CharField(max_length=50) | |||
code = models.TextField() | |||
meta = models.TextField(blank=True, null=True) | |||
description = models.TextField(blank=True, null=True) | |||
stylized = models.TextField(blank=True, null=True) | |||
lexer = models.CharField(max_length=50) | |||
code = models.TextField() | |||
meta = models.TextField(blank=True, null=True) | |||
description = models.TextField(blank=True, null=True) | |||
stylized = models.TextField(blank=True, null=True) | |||
stylized_min = models.TextField(blank=True, null=True) | |||
embedded = models.TextField(blank=True, null=True) | |||
line_count = models.IntegerField(blank=True, null=True, default=None) | |||
embedded = models.TextField(blank=True, null=True) | |||
line_count = models.IntegerField(blank=True, null=True, default=None) | |||
key = models.CharField(max_length=100, blank=True, null=True) | |||
public = models.BooleanField(default=False) | |||
blog_post = models.BooleanField(default=False) | |||
key = models.CharField(max_length=100, blank=True, null=True) | |||
public = models.BooleanField(default=False) | |||
blog_post = models.BooleanField(default=False) | |||
views = models.IntegerField(default=0) | |||
created = models.DateTimeField(auto_now_add=True, editable=False) | |||
modified = models.DateTimeField(auto_now=True, editable=False) | |||
views = models.IntegerField(default=0) | |||
created = models.DateTimeField(auto_now_add=True, editable=False) | |||
modified = models.DateTimeField(auto_now=True, editable=False) | |||
publish_date = models.DateTimeField(blank=True, null=True) | |||
def save(self, *args, **kwargs): | |||
@@ -53,7 +57,9 @@ class Snipt(models.Model): | |||
self.slug = slugify_uniquely(self.title, Snipt) | |||
if not self.key: | |||
self.key = hashlib.md5(self.slug + str(datetime.datetime.now()) + str(random.random())).hexdigest() | |||
self.key = hashlib.md5(self.slug + | |||
str(datetime.datetime.now()) + | |||
str(random.random())).hexdigest() | |||
if self.lexer == 'markdown': | |||
self.stylized = markdown(self.code, 'default') | |||
@@ -61,21 +67,46 @@ class Snipt(models.Model): | |||
# Snipt embeds | |||
for match in re.findall('\[\[(\w{32})\]\]', self.stylized): | |||
self.stylized = self.stylized.replace('[[' + str(match) + ']]', | |||
'<script type="text/javascript" src="https://snipt.net/embed/{}/?snipt"></script><div id="snipt-embed-{}"></div>'.format(match, match)) | |||
""" | |||
<script type="text/javascript" | |||
src="https://snipt.net/embed/{}/?snipt"> | |||
</script> | |||
<div id="snipt-embed-{}"></div>""".format(match, match)) | |||
# YouTube embeds | |||
for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]', self.stylized): | |||
self.stylized = self.stylized.replace('[[youtube-{}-{}x{}]]'.format(str(match[0]), str(match[1]), str(match[2])), | |||
'<iframe width="{}" height="{}" src="https://www.youtube.com/embed/{}" frameborder="0" allowfullscreen></iframe>'.format(match[1], match[2], match[0])) | |||
for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]', | |||
self.stylized): | |||
self.stylized = self.stylized \ | |||
.replace('[[youtube-{}-{}x{}]]'.format( | |||
str(match[0]), | |||
str(match[1]), | |||
str(match[2])), | |||
"""<iframe width="{}" height="{}" | |||
src="https://www.youtube.com/embed/{}" | |||
frameborder="0" allowfullscreen></iframe>""" | |||
.format(match[1], match[2], match[0])) | |||
# Vimeo embeds | |||
for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]', self.stylized): | |||
self.stylized = self.stylized.replace('[[vimeo-{}-{}x{}]]'.format(str(match[0]), str(match[1]), str(match[2])), | |||
'<iframe src="https://player.vimeo.com/video/{}" width="{}" height="{}" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'.format(match[0], match[1], match[2])) | |||
for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]', | |||
self.stylized): | |||
self.stylized = self.stylized \ | |||
.replace('[[vimeo-{}-{}x{}]]'.format( | |||
str(match[0]), | |||
str(match[1]), | |||
str(match[2])), | |||
"""<iframe src="https://player.vimeo.com/video/{}" | |||
width="{}" height="{}" frameborder="0" | |||
webkitAllowFullScreen mozallowfullscreen | |||
allowFullScreen></iframe>""" | |||
.format(match[0], match[1], match[2])) | |||
# Tweet embeds | |||
for match in re.findall('\[\[tweet-(\d+)\]\]', self.stylized): | |||
self.stylized = self.stylized.replace('[[tweet-{}]]'.format(str(match)), '<div class="embedded-tweet" data-tweet-id="{}"></div>'.format(str(match))) | |||
self.stylized = self.stylized \ | |||
.replace( | |||
'[[tweet-{}]]'.format(str(match)), | |||
'<div class="embedded-tweet" data-tweet-id="{}"></div>' | |||
.format(str(match))) | |||
# Parse Snipt usernames | |||
for match in re.findall('@(\w+) ', self.stylized): | |||
@@ -85,17 +116,19 @@ class Snipt(models.Model): | |||
if user: | |||
url = user.profile.get_user_profile_url() | |||
self.stylized = self.stylized.replace('@{} '.format(str(match)), '<a href="{}">@{}</a> '.format(url, match)) | |||
self.stylized = self.stylized \ | |||
.replace('@{} '.format(str(match)), | |||
'<a href="{}">@{}</a> '.format(url, match)) | |||
else: | |||
self.stylized = highlight(self.code, | |||
get_lexer_by_name(self.lexer, encoding='UTF-8'), | |||
get_lexer_by_name(self.lexer, | |||
encoding='UTF-8'), | |||
HtmlFormatter(linenos='table', | |||
anchorlinenos=True, | |||
lineanchors='L', | |||
linespans='L', | |||
) | |||
) | |||
anchorlinenos=True, | |||
lineanchors='L', | |||
linespans='L', | |||
)) | |||
self.line_count = len(self.code.split('\n')) | |||
if self.lexer == 'markdown': | |||
@@ -104,7 +137,8 @@ class Snipt(models.Model): | |||
lexer_for_embedded = self.lexer | |||
embedded = highlight(self.code, | |||
get_lexer_by_name(lexer_for_embedded, encoding='UTF-8'), | |||
get_lexer_by_name(lexer_for_embedded, | |||
encoding='UTF-8'), | |||
HtmlFormatter( | |||
style='native', | |||
noclasses=True, | |||
@@ -120,8 +154,8 @@ class Snipt(models.Model): | |||
-webkit-border-radius: 5px; | |||
-moz-border-radius: 5px; | |||
""")) | |||
embedded = (embedded.replace("\\\"","\\\\\"") | |||
.replace('\'','\\\'') | |||
embedded = (embedded.replace("\\\"", "\\\\\"") | |||
.replace('\'', '\\\'') | |||
.replace("\\", "\\\\") | |||
.replace('background: #202020', '')) | |||
self.embedded = embedded | |||
@@ -139,18 +173,23 @@ class Snipt(models.Model): | |||
if self.lexer == 'markdown': | |||
self.stylized_min = markdown(self.code[:1000], 'default') | |||
else: | |||
self.stylized_min = highlight(self.code[:1000], | |||
get_lexer_by_name(self.lexer, encoding='UTF-8'), | |||
HtmlFormatter(linenos='table', linenospecial=1, lineanchors='line')) | |||
self.stylized_min = highlight( | |||
self.code[:1000], | |||
get_lexer_by_name(self.lexer, encoding='UTF-8'), | |||
HtmlFormatter(linenos='table', | |||
linenospecial=1, | |||
lineanchors='line')) | |||
return self.stylized_min | |||
def get_absolute_url(self): | |||
if self.blog_post: | |||
if self.user.profile.blog_domain: | |||
return u'http://{}/{}/'.format(self.user.profile.blog_domain.split(' ')[0], self.slug) | |||
return u'http://{}/{}/'.format( | |||
self.user.profile.blog_domain.split(' ')[0], self.slug) | |||
else: | |||
return u'https://{}.snipt.net/{}/'.format(self.user.username.replace('_', '-'), self.slug) | |||
return u'https://{}.snipt.net/{}/'.format( | |||
self.user.username.replace('_', '-'), self.slug) | |||
if self.custom_slug: | |||
return u'/{}/'.format(self.custom_slug) | |||
@@ -158,15 +197,18 @@ class Snipt(models.Model): | |||
if self.public: | |||
return u'/{}/{}/'.format(self.user.username, self.slug) | |||
else: | |||
return u'/{}/{}/?key={}'.format(self.user.username, self.slug, self.key) | |||
return u'/{}/{}/?key={}'.format( | |||
self.user.username, self.slug, self.key) | |||
def get_full_absolute_url(self): | |||
if self.blog_post: | |||
if self.user.profile.blog_domain: | |||
return u'http://{}/{}/'.format(self.user.profile.blog_domain.split(' ')[0], self.slug) | |||
return u'http://{}/{}/'.format( | |||
self.user.profile.blog_domain.split(' ')[0], self.slug) | |||
else: | |||
return u'https://{}.snipt.net/{}/'.format(self.user.username, self.slug) | |||
return u'https://{}.snipt.net/{}/'.format( | |||
self.user.username, self.slug) | |||
if settings.DEBUG: | |||
root = 'http://local.snipt.net' | |||
@@ -176,7 +218,10 @@ class Snipt(models.Model): | |||
if self.public: | |||
return u'{}/{}/{}/'.format(root, self.user.username, self.slug) | |||
else: | |||
return u'{}/{}/{}/?key={}'.format(root, self.user.username, self.slug, self.key) | |||
return u'{}/{}/{}/?key={}'.format(root, | |||
self.user.username, | |||
self.slug, | |||
self.key) | |||
def get_download_url(self): | |||
@@ -233,12 +278,14 @@ class Snipt(models.Model): | |||
else: | |||
return get_lexer_by_name(self.lexer).name | |||
class Favorite(models.Model): | |||
snipt = models.ForeignKey(Snipt) | |||
user = models.ForeignKey(User) | |||
user = models.ForeignKey(User) | |||
created = models.DateTimeField(auto_now_add=True, editable=False) | |||
created = models.DateTimeField(auto_now_add=True, editable=False) | |||
modified = models.DateTimeField(auto_now=True, editable=False) | |||
def __unicode__(self): | |||
return u'{} favorited by {}'.format(self.snipt.title, self.user.username) | |||
return u'{} favorited by {}'.format(self.snipt.title, | |||
self.user.username) |
@@ -15,4 +15,5 @@ class SniptIndex(indexes.SearchIndex, indexes.Indexable): | |||
def index_queryset(self, **kwargs): | |||
"""Used when the entire index for model is updated.""" | |||
return self.get_model().objects.filter(created__lte=datetime.datetime.now()) | |||
return self.get_model().objects.filter( | |||
created__lte=datetime.datetime.now()) |
@@ -11,6 +11,7 @@ import hashlib | |||
register = template.Library() | |||
@tag(register, [Constant('as'), Variable()]) | |||
def snipt_is_favorited_by_user(context, asvar): | |||
@@ -30,6 +31,7 @@ def snipt_is_favorited_by_user(context, asvar): | |||
return '' | |||
@tag(register, []) | |||
def snipts_count_for_user(context): | |||
@@ -42,11 +44,13 @@ def snipts_count_for_user(context): | |||
return snipts | |||
@tag(register, [Constant('as'), Variable()]) | |||
def get_lexers(context, asvar): | |||
context[asvar] = get_lexers_list() | |||
return '' | |||
@tag(register, [Constant('for'), Variable()]) | |||
def generate_line_numbers(context, line_numbers): | |||
html = '' | |||
@@ -56,6 +60,7 @@ def generate_line_numbers(context, line_numbers): | |||
return html | |||
@register.filter | |||
def md5(string): | |||
return hashlib.md5(string.lower()).hexdigest() |
@@ -11,11 +11,19 @@ class SniptResourceTest(ResourceTestCase): | |||
super(SniptResourceTest, self).setUp() | |||
# Johnny | |||
self.johnny = User.objects.create_user('johnny', 'johnny@snipt.net', 'password') | |||
self.johnny = User.objects.create_user('johnny', 'johnny@snipt.net', | |||
'password') | |||
ApiKey.objects.get_or_create(user=self.johnny) | |||
self.johnny_auth = self.create_apikey(self.johnny, self.johnny.api_key.key) | |||
self.johnny_private = Snipt(title='Private snipt for Johnny', lexer='text', public=False, user=self.johnny) | |||
self.johnny_public = Snipt(title='Public snipt for Johnny', lexer='text', public=True, user=self.johnny) | |||
self.johnny_auth = self.create_apikey(self.johnny, | |||
self.johnny.api_key.key) | |||
self.johnny_private = Snipt(title='Private snipt for Johnny', | |||
lexer='text', | |||
public=False, | |||
user=self.johnny) | |||
self.johnny_public = Snipt(title='Public snipt for Johnny', | |||
lexer='text', | |||
public=True, | |||
user=self.johnny) | |||
self.johnny_private.save() | |||
self.johnny_public.save() | |||
@@ -23,16 +31,21 @@ class SniptResourceTest(ResourceTestCase): | |||
self.bob = User.objects.create_user('bob', 'bob@snipt.net', 'password') | |||
ApiKey.objects.get_or_create(user=self.bob) | |||
self.bob_auth = self.create_apikey(self.bob, self.bob.api_key.key) | |||
self.bob_private = Snipt(title='Private snipt for Bob', lexer='text', public=False, user=self.bob) | |||
self.bob_public = Snipt(title='Public snipt for Bob', lexer='text', public=True, user=self.bob) | |||
self.bob_private = Snipt(title='Private snipt for Bob', | |||
lexer='text', | |||
public=False, | |||
user=self.bob) | |||
self.bob_public = Snipt(title='Public snipt for Bob', | |||
lexer='text', | |||
public=True, | |||
user=self.bob) | |||
self.bob_private.save() | |||
self.bob_public.save() | |||
# Private | |||
def test_get_private_list(self): | |||
resp = self.api_client.get('/api/private/snipt/', format='json', authentication=self.johnny_auth) | |||
resp = self.api_client.get('/api/private/snipt/', format='json', | |||
authentication=self.johnny_auth) | |||
self.assertHttpOK(resp) | |||
self.assertValidJSONResponse(resp) | |||
@@ -40,18 +53,27 @@ class SniptResourceTest(ResourceTestCase): | |||
def test_get_private_detail(self): | |||
resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json', authentication=self.johnny_auth) | |||
resp = self.api_client.get( | |||
'/api/private/snipt/{}/'.format(self.johnny_private.pk), | |||
format='json', | |||
authentication=self.johnny_auth) | |||
self.assertHttpOK(resp) | |||
self.assertValidJSONResponse(resp) | |||
self.assertEqual(self.deserialize(resp)['key'], self.johnny_private.key) | |||
self.assertEqual(self.deserialize(resp)['key'], | |||
self.johnny_private.key) | |||
# Unauthenticated request. | |||
resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json') | |||
resp = self.api_client.get( | |||
'/api/private/snipt/{}/'.format(self.johnny_private.pk), | |||
format='json') | |||
self.assertHttpUnauthorized(resp) | |||
# Unauthorized request. | |||
resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json', authentication=self.bob_auth) | |||
resp = self.api_client.get( | |||
'/api/private/snipt/{}/'.format(self.johnny_private.pk), | |||
format='json', | |||
authentication=self.bob_auth) | |||
self.assertHttpUnauthorized(resp) | |||
def test_post_private_list(self): | |||
@@ -63,21 +85,21 @@ class SniptResourceTest(ResourceTestCase): | |||
} | |||
resp = self.api_client.post('/api/private/snipt/', | |||
data=new_snipt, | |||
format='json', | |||
authentication=self.johnny_auth) | |||
data=new_snipt, | |||
format='json', | |||
authentication=self.johnny_auth) | |||
self.assertHttpCreated(resp) | |||
self.assertEqual(Snipt.objects.count(), 5) | |||
resp = self.api_client.get('/api/private/snipt/', format='json', authentication=self.johnny_auth) | |||
resp = self.api_client.get('/api/private/snipt/', | |||
format='json', | |||
authentication=self.johnny_auth) | |||
self.assertEqual(len(self.deserialize(resp)['objects']), 3) | |||
resp = self.api_client.get('/api/public/snipt/', format='json') | |||
self.assertEqual(len(self.deserialize(resp)['objects']), 2) | |||
# Public | |||
def test_get_public_list(self): | |||
self.assertEqual(Snipt.objects.count(), 4) | |||
@@ -3,23 +3,46 @@ from django.conf.urls import * | |||
from snipts import views | |||
urlpatterns = patterns('', | |||
# Redirects | |||
url(r'^s/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', views.redirect_snipt, name='redirect-snipt'), | |||
url(r'^(?P<username>[^/]+)/feed/$', views.redirect_user_feed, name='redirect-feed'), | |||
url(r'^public/tag/(?P<tag_slug>[^/]+)/feed/$', views.redirect_public_tag_feed, name='redirect-public-tag-feed'), | |||
url(r'^(?P<username>[^/]+)/tag/(?P<tag_slug>[^/]+)/feed/$', views.redirect_user_tag_feed, name='redirect-user-tag-feed'), | |||
url(r'^public/$', views.list_public, name='list-public'), | |||
url(r'^public/tag/(?P<tag_slug>[^/]+)/$', views.list_public, name='list-public-tag'), | |||
url(r'^download/(?P<snipt_key>[^/]+).*$', views.download, name='download'), | |||
url(r'^embed/(?P<snipt_key>[^/]+)/$', views.embed, name='embed'), | |||
url(r'^raw/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', views.raw, name='raw'), | |||
url(r'^(?P<username_or_custom_slug>[^/]+)/$', views.list_user, name='list-user'), | |||
url(r'^(?P<username_or_custom_slug>[^/]+)/tag/(?P<tag_slug>[^/]+)/$', views.list_user, name='list-user-tag'), | |||
url(r'^(?P<username>[^/]+)/favorites/$', views.favorites, name='favorites'), | |||
url(r'^(?P<username>[^/]+)/blog-posts/$', views.blog_posts, name='blog-posts'), | |||
url(r'^(?P<username>[^/]+)/(?P<snipt_slug>[^/]+)/$', views.detail, name='detail'), | |||
) | |||
urlpatterns = \ | |||
patterns('', | |||
url(r'^s/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', | |||
views.redirect_snipt, name='redirect-snipt'), | |||
url(r'^(?P<username>[^/]+)/feed/$', | |||
views.redirect_user_feed, | |||
name='redirect-feed'), | |||
url(r'^public/tag/(?P<tag_slug>[^/]+)/feed/$', | |||
views.redirect_public_tag_feed, | |||
name='redirect-public-tag-feed'), | |||
url(r'^(?P<username>[^/]+)/tag/(?P<tag_slug>[^/]+)/feed/$', | |||
views.redirect_user_tag_feed, | |||
name='redirect-user-tag-feed'), | |||
url(r'^public/$', | |||
views.list_public, | |||
name='list-public'), | |||
url(r'^public/tag/(?P<tag_slug>[^/]+)/$', | |||
views.list_public, | |||
name='list-public-tag'), | |||
url(r'^download/(?P<snipt_key>[^/]+).*$', | |||
views.download, | |||
name='download'), | |||
url(r'^embed/(?P<snipt_key>[^/]+)/$', | |||
views.embed, | |||
name='embed'), | |||
url(r'^raw/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', | |||
views.raw, | |||
name='raw'), | |||
url(r'^(?P<username_or_custom_slug>[^/]+)/$', | |||
views.list_user, | |||
name='list-user'), | |||
url(r'^(?P<username_or_custom_slug>[^/]+)/tag/(?P<tag_slug>[^/]+)/$', | |||
views.list_user, | |||
name='list-user-tag'), | |||
url(r'^(?P<username>[^/]+)/favorites/$', | |||
views.favorites, | |||
name='favorites'), | |||
url(r'^(?P<username>[^/]+)/blog-posts/$', | |||
views.blog_posts, | |||
name='blog-posts'), | |||
url(r'^(?P<username>[^/]+)/(?P<snipt_slug>[^/]+)/$', | |||
views.detail, | |||
name='detail')) |
@@ -17,6 +17,7 @@ def slugify_uniquely(value, model, slugfield="slug"): | |||
return potential | |||
suffix = str(uuid.uuid4()).split('-')[0] | |||
def activate_user(user, request, **kwargs): | |||
user.is_active = True | |||
user.save() | |||
@@ -25,6 +26,7 @@ def activate_user(user, request, **kwargs): | |||
password=request.POST['password1']) | |||
login(request, user) | |||
def get_lexers_list(): | |||
lexers = list(get_all_lexers()) | |||
@@ -62,10 +62,12 @@ def detail(request, username, snipt_slug): | |||
'user': user, | |||
} | |||
def download(request, snipt_key): | |||
snipt = get_object_or_404(Snipt, key=snipt_key) | |||
return HttpResponse(snipt.code, content_type='application/x-download') | |||
def embed(request, snipt_key): | |||
snipt = get_object_or_404(Snipt, key=snipt_key) | |||
@@ -75,6 +77,7 @@ def embed(request, snipt_key): | |||
context_instance=RequestContext(request), | |||
content_type='application/javascript') | |||
@render_to('snipts/list-user.html') | |||
def blog_posts(request, username): | |||
@@ -92,7 +95,8 @@ def blog_posts(request, username): | |||
public_user = True | |||
user = get_object_or_404(User, username=username) | |||
snipts = Snipt.objects.filter(blog_post=True, user=user, public=True) | |||
tags = Tag.objects.filter(snipt__user=user, snipt__public=True).distinct() | |||
tags = Tag.objects.filter(snipt__user=user, | |||
snipt__public=True).distinct() | |||
tags = tags.order_by('name') | |||
snipts = snipts.order_by('-created') | |||
@@ -112,6 +116,7 @@ def blog_posts(request, username): | |||
return context | |||
@render_to('snipts/list-user.html') | |||
def favorites(request, username): | |||
@@ -148,6 +153,7 @@ def favorites(request, username): | |||
return context | |||
@render_to('snipts/list-public.html') | |||
def list_public(request, tag_slug=None): | |||
@@ -175,6 +181,7 @@ def list_public(request, tag_slug=None): | |||
return context | |||
@render_to('snipts/list-user.html') | |||
def list_user(request, username_or_custom_slug, tag_slug=None): | |||
@@ -190,7 +197,8 @@ def list_user(request, username_or_custom_slug, tag_slug=None): | |||
tags = Tag.objects | |||
snipts = Snipt.objects | |||
if user == request.user or (request.GET.get('api_key') == user.api_key.key): | |||
if user == request.user or \ | |||
(request.GET.get('api_key') == user.api_key.key): | |||
public = False | |||
favorites = Favorite.objects.filter(user=user).values('snipt') | |||
@@ -232,6 +240,7 @@ def list_user(request, username_or_custom_slug, tag_slug=None): | |||
return context | |||
def raw(request, snipt_key, lexer=None): | |||
snipt = get_object_or_404(Snipt, key=snipt_key) | |||
@@ -250,23 +259,23 @@ def raw(request, snipt_key, lexer=None): | |||
snipt.lexer = lexer | |||
snipt.save() | |||
content_type='text/plain' | |||
content_type = 'text/plain' | |||
if 'nice' in request.GET: | |||
content_type='text/html' | |||
content_type = 'text/html' | |||
return render_to_response('snipts/raw.html', | |||
{'snipt': snipt}, | |||
context_instance=RequestContext(request), | |||
content_type=content_type) | |||
def rss(request, context): | |||
return render_to_response( | |||
'rss.xml', | |||
context, | |||
context_instance=RequestContext(request), | |||
content_type="application/rss+xml" | |||
) | |||
return render_to_response('rss.xml', | |||
context, | |||
context_instance=RequestContext(request), | |||
content_type="application/rss+xml") | |||
def search(request, template='search/search.html', load_all=True, | |||
form_class=ModelSearchForm, searchqueryset=None, | |||
@@ -279,12 +288,18 @@ def search(request, template='search/search.html', load_all=True, | |||
# We have a query. | |||
if request.GET.get('q'): | |||
if request.user.is_authenticated() and '--mine' in request.GET.get('q'): | |||
searchqueryset = SearchQuerySet().filter(author=request.user).order_by('-pub_date') | |||
if request.user.is_authenticated() and '--mine' in \ | |||
request.GET.get('q'): | |||
searchqueryset = SearchQuerySet().filter(author=request.user) \ | |||
.order_by('-pub_date') | |||
else: | |||
searchqueryset = SearchQuerySet().filter(Q(public=True) | Q(author=request.user)).order_by('-pub_date') | |||
searchqueryset = SearchQuerySet() \ | |||
.filter(Q(public=True) | Q(author=request.user)) \ | |||
.order_by('-pub_date') | |||
form = ModelSearchForm(request.GET, searchqueryset=searchqueryset, load_all=load_all) | |||
form = ModelSearchForm(request.GET, | |||
searchqueryset=searchqueryset, | |||
load_all=load_all) | |||
if form.is_valid(): | |||
query = form.cleaned_data['q'] | |||
@@ -314,19 +329,24 @@ def search(request, template='search/search.html', load_all=True, | |||
if extra_context: | |||
context.update(extra_context) | |||
return render_to_response(template, context, context_instance=context_class(request)) | |||
return render_to_response(template, | |||
context, | |||
context_instance=context_class(request)) | |||
def redirect_snipt(request, snipt_key, lexer=None): | |||
snipt = get_object_or_404(Snipt, key=snipt_key) | |||
return HttpResponseRedirect(snipt.get_absolute_url()) | |||
def redirect_public_tag_feed(request, tag_slug): | |||
return HttpResponseRedirect('/public/tag/{}/?rss'.format(tag_slug)) | |||
def redirect_user_feed(request, username): | |||
user = get_object_or_404(User, username=username) | |||
return HttpResponseRedirect(user.get_absolute_url() + '?rss') | |||
def redirect_user_tag_feed(request, username, tag_slug): | |||
return HttpResponseRedirect(u'/{}/tag/{}/?rss'.format(username, tag_slug)) |
@@ -66,7 +66,6 @@ | |||
{% endif %} | |||
{% else %} | |||
<script> | |||
document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>') | |||
window.ll = function() {}; | |||
</script> | |||
{% endif %} |
@@ -1,6 +1,4 @@ | |||
from django.conf import settings | |||
from django.conf.urls import include, patterns, url | |||
from django.conf.urls.static import static | |||
from django.views.generic import TemplateView | |||
from django.http import HttpResponseRedirect | |||
from django.contrib import admin | |||
@@ -11,11 +9,9 @@ from snipts.api import (PublicSniptResource, | |||
from snipts.views import search | |||
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, for_teams, for_teams_complete) | |||
import admin as custom_admin | |||
import os | |||
@@ -32,47 +28,56 @@ private_api.register(PrivateUserResource()) | |||
private_api.register(PrivateFavoriteResource()) | |||
private_api.register(PrivateUserProfileResource()) | |||
urlpatterns = patterns('', | |||
urlpatterns = \ | |||
patterns('', | |||
url(r'^$', homepage), | |||
url(r'^login-redirect/$', login_redirect), | |||
url(r'^$', homepage), | |||
url(r'^login-redirect/$', login_redirect), | |||
url(r'^admin/', include(admin.site.urls)), | |||
url(r'^admin/', include(admin.site.urls)), | |||
url(r'^404/$', TemplateView.as_view(template_name='404.html')), | |||
url(r'^500/$', TemplateView.as_view(template_name='500.html')), | |||
url(r'^404/$', TemplateView.as_view(template_name='404.html')), | |||
url(r'^500/$', TemplateView.as_view(template_name='500.html')), | |||
url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')), | |||
url(r'^humans.txt$', TemplateView.as_view(template_name='humans.txt')), | |||
url(r'^sitemap.xml$', sitemap), | |||
url(r'^tags/$', tags), | |||
url(r'^robots.txt$', | |||
TemplateView.as_view(template_name='robots.txt')), | |||
url(r'^humans.txt$', | |||
TemplateView.as_view(template_name='humans.txt')), | |||
url(r'^sitemap.xml$', sitemap), | |||
url(r'^tags/$', tags), | |||
url(r'^pro/$', pro), | |||
url(r'^pro/complete/$', pro_complete), | |||
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'^for-teams/$', for_teams), | |||
url(r'^for-teams/complete/$', for_teams_complete), | |||
url(r'^account/', include('accounts.urls')), | |||
url(r'^account/', include('accounts.urls')), | |||
url(r'^api/public/lexer/$', lexers), | |||
url(r'^api/public/lexer/$', lexers), | |||
url(r'^api/private/key/$', user_api_key), | |||
url(r'^api/', include(public_api.urls)), | |||
url(r'^api/', include(private_api.urls)), | |||
url(r'^api/private/key/$', user_api_key), | |||
url(r'^api/', include(public_api.urls)), | |||
url(r'^api/', include(private_api.urls)), | |||
url(r'^search/$', search), | |||
url(r'^search/$', search), | |||
url(r'^register/$', lambda x: HttpResponseRedirect('/signup/')), | |||
url(r'^signup/$', SniptRegistrationView.as_view(), | |||
name='registration_register'), | |||
url(r'', include('registration.backends.default.urls')), | |||
url(r'^register/$', lambda x: HttpResponseRedirect('/signup/')), | |||
url(r'^signup/$', SniptRegistrationView.as_view(), | |||
name='registration_register'), | |||
url(r'', include('registration.backends.default.urls')), | |||
url(r'^', include('snipts.urls')), | |||
url(r'^', include('snipts.urls')), | |||
url(r'^(?P<path>favicon\.ico)$', 'django.views.static.serve', {'document_root': os.path.join(os.path.dirname(__file__), 'static/img')}), | |||
) | |||
url(r'^(?P<path>favicon\.ico)$', 'django.views.static.serve', { | |||
'document_root': os.path.join(os.path.dirname(__file__), | |||
'static/img') | |||
}), | |||
) | |||
urlpatterns += patterns('', | |||
(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': os.path.join(os.path.dirname(__file__), 'media')}), | |||
) | |||
urlpatterns += \ | |||
patterns('', | |||
(r'^static/(?P<path>.*)$', 'django.views.static.serve', { | |||
'document_root': os.path.join(os.path.dirname(__file__), | |||
'media') | |||
})) |
@@ -1,6 +1,6 @@ | |||
from django.conf import settings | |||
from django.contrib.auth.models import User | |||
class EmailOrUsernameModelBackend(object): | |||
def authenticate(self, username=None, password=None): | |||
if '@' in username: | |||
@@ -3,21 +3,22 @@ from registration.forms import RegistrationForm | |||
from django.contrib.auth.models import User | |||
from django import forms | |||
class SniptRegistrationForm(RegistrationForm): | |||
""" | |||
Subclass of ``RegistrationForm`` which enforces uniqueness of | |||
email addresses and further restricts usernames. | |||
""" | |||
def clean_username(self): | |||
""" | |||
Validate that the username is alphanumeric and is not already | |||
in use. | |||
""" | |||
existing = User.objects.filter(username__iexact=self.cleaned_data['username']) | |||
existing = User.objects.filter( | |||
username__iexact=self.cleaned_data['username']) | |||
if existing.exists(): | |||
raise forms.ValidationError(_("A user with that username already exists.")) | |||
raise forms.ValidationError( | |||
_("A user with that username already exists.")) | |||
elif '@' in self.cleaned_data['username']: | |||
raise forms.ValidationError(_("Cannot have '@' in username.")) | |||
@@ -33,8 +34,9 @@ class SniptRegistrationForm(RegistrationForm): | |||
""" | |||
Validate that the supplied email address is unique for the | |||
site. | |||
""" | |||
if User.objects.filter(email__iexact=self.cleaned_data['email']): | |||
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address.")) | |||
raise forms.ValidationError( | |||
_("""This email address is already in use. Please supply a | |||
different email address.""")) | |||
return self.cleaned_data['email'] |
@@ -1,10 +1,16 @@ | |||
from django.conf import settings | |||
from django import template | |||
import hmac, hashlib, os | |||
import hmac | |||
import hashlib | |||
import os | |||
register = template.Library() | |||
@register.filter | |||
def intercom_sha_256(user_id): | |||
return hmac.new(os.environ.get('INTERCOM_SECRET_KEY', settings.INTERCOM_SECRET_KEY), str(user_id), digestmod=hashlib.sha256).hexdigest() | |||
return hmac.new(os.environ.get('INTERCOM_SECRET_KEY', | |||
settings.INTERCOM_SECRET_KEY), | |||
str(user_id), | |||
digestmod=hashlib.sha256).hexdigest() |
@@ -20,7 +20,7 @@ class VerbatimNode(template.Node): | |||
def __init__(self, text): | |||
self.text = text | |||
def render(self, context): | |||
return self.text | |||
@@ -1,9 +1,9 @@ | |||
from registration.backends.default.views import RegistrationView | |||
from utils.forms import SniptRegistrationForm | |||
class SniptRegistrationView(RegistrationView): | |||
""" | |||
Custom registration view that uses our custom form. | |||
""" | |||
form_class = SniptRegistrationForm |
@@ -1,10 +1,9 @@ | |||
from accounts.models import UserProfile | |||
from annoying.decorators import ajax_request | |||
from annoying.decorators import ajax_request, render_to | |||
from blogs.views import blog_list | |||
from django.http import HttpResponseRedirect, HttpResponseBadRequest | |||
from django.conf import settings | |||
from django.contrib.auth.decorators import login_required | |||
from annoying.decorators import ajax_request, render_to | |||
from django.shortcuts import render_to_response | |||
from django.template import RequestContext | |||
from snipts.utils import get_lexers_list | |||
@@ -19,6 +18,7 @@ import hashlib | |||
import os | |||
import stripe | |||
@render_to('for-teams.html') | |||
def for_teams(request): | |||
if request.user.is_authenticated(): | |||
@@ -27,6 +27,7 @@ def for_teams(request): | |||
profile.save() | |||
return {} | |||
@render_to('for-teams-complete.html') | |||
def for_teams_complete(request): | |||
@@ -43,7 +44,8 @@ def for_teams_complete(request): | |||
Info: | |||
%s | |||
""" % (request.user.username, request.user.email, name, members, info), 'support@snipt.net', | |||
""" % (request.user.username, request.user.email, name, members, | |||
info), 'support@snipt.net', | |||
['nick@nicksergeant.com'], fail_silently=False) | |||
profile = request.user.profile | |||
@@ -70,6 +72,7 @@ def for_teams_complete(request): | |||
else: | |||
return HttpResponseBadRequest() | |||
@render_to('homepage.html') | |||
def homepage(request): | |||
@@ -99,6 +102,7 @@ def homepage(request): | |||
'users_count': User.objects.all().count(), | |||
} | |||
@ajax_request | |||
def lexers(request): | |||
lexers = get_lexers_list() | |||
@@ -125,12 +129,14 @@ def lexers(request): | |||
return {'objects': objects} | |||