So much PEP8.

master
Nick Sergeant 2015-07-24 21:28:31 -04:00
parent 9e0fded98e
commit 6894f44349
39 changed files with 514 additions and 491 deletions

View File

@ -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',)

View File

@ -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])

View File

@ -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'))

View File

@ -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 = {

View File

@ -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']

View File

@ -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)

View File

@ -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'))

View File

@ -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')
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 = 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')
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)
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 = 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")

View File

View File

@ -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)

View File

@ -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()

View File

@ -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()),
],
),
]

View File

@ -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

View File

@ -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&hellip;
</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 %}

View File

@ -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)

View File

@ -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

View File

@ -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',
)

View File

@ -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

View File

@ -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',)

View File

@ -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)

View File

@ -1,6 +1,7 @@
from django.forms import ModelForm
from snipts.models import Snipt
class SniptForm(ModelForm):
class Meta:
model = Snipt

View File

@ -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)

View File

@ -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())

View File

@ -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()

View File

@ -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)

View File

@ -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'))

View File

@ -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())

View File

@ -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))

View File

@ -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 %}

73
urls.py
View File

@ -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')
}))

View File

@ -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:

View File

@ -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']

View File

@ -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()

View File

@ -20,7 +20,7 @@ class VerbatimNode(template.Node):
def __init__(self, text):
self.text = text
def render(self, context):
return self.text

View File

@ -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

View File

@ -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}
def login_redirect(request):
if request.user.is_authenticated():
return HttpResponseRedirect('/' + request.user.username + '/')
else:
return HttpResponseRedirect('/')
@login_required
@render_to('pro.html')
def pro(request):
@ -138,6 +144,7 @@ def pro(request):
return HttpResponseRedirect('/' + request.user.username + '/')
return {}
@login_required
@render_to('pro-complete.html')
def pro_complete(request):
@ -145,7 +152,8 @@ def pro_complete(request):
if request.method == 'POST':
token = request.POST['token']
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)
if 'plan' in request.GET:
plan = request.GET['plan']
@ -158,7 +166,8 @@ def pro_complete(request):
email=request.user.email)
except stripe.CardError, e:
error_message = e.json_body['error']['message']
return HttpResponseRedirect('/pro/?declined=%s' % error_message or 'Your card was declined.')
return HttpResponseRedirect('/pro/?declined=%s' % error_message or
'Your card was declined.')
profile = request.user.profile
profile.is_pro = True
@ -171,6 +180,7 @@ def pro_complete(request):
else:
return HttpResponseBadRequest()
def sitemap(request):
tags = Tag.objects.filter(snipt__public=True)
@ -182,6 +192,7 @@ def sitemap(request):
context_instance=RequestContext(request),
content_type='application/xml')
@render_to('tags.html')
def tags(request):
@ -198,13 +209,14 @@ 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
}
if not request.user.is_authenticated():
return HttpResponseBadRequest()
return {
'api_key': request.user.api_key.key
}