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 from accounts.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin): 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'] list_filter = ['teams_beta_seen', 'teams_beta_applied']
search_fields = ('user__username', 'gittip_username',) search_fields = ('user__username', 'gittip_username',)

View File

@ -1,9 +1,9 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from datetime import date
from datetime import datetime from datetime import datetime
from django.db import models from django.db import models
from snipts.models import Snipt from snipts.models import Snipt
class UserProfile(models.Model): class UserProfile(models.Model):
LIST_VIEW_CHOICES = ( LIST_VIEW_CHOICES = (
@ -42,38 +42,45 @@ class UserProfile(models.Model):
) )
# User # User
user = models.OneToOneField(User) user = models.OneToOneField(User)
is_pro = models.BooleanField(default=False) is_pro = models.BooleanField(default=False)
teams_beta_seen = models.BooleanField(default=False) teams_beta_seen = models.BooleanField(default=False)
teams_beta_applied = models.BooleanField(default=False) teams_beta_applied = models.BooleanField(default=False)
pro_date = models.DateTimeField(blank=True, null=True) pro_date = models.DateTimeField(blank=True, null=True)
stripe_id = models.CharField(max_length=100, null=True, blank=True) stripe_id = models.CharField(max_length=100, null=True, blank=True)
has_gravatar = models.BooleanField(default=False) has_gravatar = models.BooleanField(default=False)
list_view = models.CharField(max_length=1, null=False, blank=False,default='N', choices=LIST_VIEW_CHOICES) list_view = models.CharField(max_length=1, null=False, blank=False,
default='N', choices=LIST_VIEW_CHOICES)
# Blog # Blog
blog_title = 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_theme = models.CharField(max_length=1, null=False, blank=False,
blog_domain = models.CharField(max_length=250, null=True, blank=True) default='A', choices=THEME_CHOICES)
blog_domain = models.CharField(max_length=250, null=True, blank=True)
# Editor # Editor
default_editor = models.CharField(max_length=250, null=False, blank=False, default='C', choices=EDITOR_CHOICES) default_editor = models.CharField(max_length=250, null=False, blank=False,
editor_theme = models.CharField(max_length=250, null=False, blank=False, default='default', choices=EDITOR_THEME_CHOICES) 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 # 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) disqus_shortname = models.CharField(max_length=250, null=True, blank=True)
google_analytics_tracking_id = models.CharField(max_length=250, null=True, blank=True) google_analytics_tracking_id = models.CharField(max_length=250, null=True,
gauges_site_id = models.CharField(max_length=250, null=True, blank=True) blank=True)
gauges_site_id = models.CharField(max_length=250, null=True, blank=True)
# Google Ads # Google Ads
google_ad_client = models.CharField(max_length=250, null=True, blank=True) 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_slot = models.CharField(max_length=250, null=True, blank=True)
google_ad_width = 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) google_ad_height = models.CharField(max_length=250, null=True, blank=True)
def get_blog_posts(self): 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): def get_primary_blog_domain(self):
if not self.blog_domain: if not self.blog_domain:
@ -98,10 +105,14 @@ class UserProfile(models.Model):
return url return url
def has_public_snipts(self): 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): 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 return delta.days
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0]) 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 from accounts import views
urlpatterns = patterns('', urlpatterns = \
url(r'^stats/$', views.stats, name='account-stats'), patterns('',
url(r'^cancel-subscription/$', views.cancel_subscription, name='cancel-subscription'), url(r'^stats/$', views.stats, name='account-stats'),
url(r'^stripe-account-details/$', views.stripe_account_details, name='stripe-account-details'), url(r'^cancel-subscription/$', views.cancel_subscription,
url(r'^', views.account, name='account-detail'), 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 annoying.decorators import ajax_request, render_to
from snipts.models import Snipt from snipts.models import Snipt
import os, stripe import os
import stripe
@login_required @login_required
@ -19,7 +20,8 @@ def cancel_subscription(request):
if request.user.profile.stripe_id is None: if request.user.profile.stripe_id is None:
return {} return {}
else: 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 = stripe.Customer.retrieve(request.user.profile.stripe_id)
customer.delete() customer.delete()
@ -28,7 +30,8 @@ def cancel_subscription(request):
profile.stripe_id = None profile.stripe_id = None
profile.save() profile.save()
return { 'deleted': True } return {'deleted': True}
@login_required @login_required
@ajax_request @ajax_request
@ -37,7 +40,8 @@ def stripe_account_details(request):
if request.user.profile.stripe_id is None: if request.user.profile.stripe_id is None:
return {} return {}
else: 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 = stripe.Customer.retrieve(request.user.profile.stripe_id)
data = { data = {

View File

@ -2,9 +2,11 @@ from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib import admin from django.contrib import admin
class UserAdmin(UserAdmin): class UserAdmin(UserAdmin):
list_display = ['username', 'email', 'first_name', 'last_name', 'last_login', list_display = ['username', 'email', 'first_name', 'last_name',
'date_joined', 'is_active', 'is_staff', 'api_key'] 'last_login', 'date_joined', 'is_active', 'is_staff',
'api_key']
list_filter = ['is_staff', 'is_superuser', 'is_active'] list_filter = ['is_staff', 'is_superuser', 'is_active']
ordering = ['-date_joined'] ordering = ['-date_joined']

View File

@ -12,19 +12,26 @@ class BlogMiddleware:
host = request.META.get('HTTP_HOST', '') host = request.META.get('HTTP_HOST', '')
host_s = host.replace('www.', '').split('.') 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 len(host_s) > 2:
if host_s[1] == 'snipt': if host_s[1] == 'snipt':
blog_user = ''.join(host_s[:-2]) blog_user = ''.join(host_s[:-2])
if '-' in blog_user: 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: 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: 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: if request.blog_user is None:
pro_users = User.objects.filter(userprofile__is_pro=True) pro_users = User.objects.filter(userprofile__is_pro=True)

View File

@ -1,5 +1,3 @@
from django.conf.urls import * from django.conf.urls import *
urlpatterns = patterns('', urlpatterns = patterns('', url(r'^$', views.blog, name='blog'))
url(r'^$', views.blog, name='blog'),
)

View File

@ -11,6 +11,7 @@ THEME_CHOICES = {
'A': 'blogs/themes/pro-adams/', 'A': 'blogs/themes/pro-adams/',
} }
def blog_list(request, username_or_custom_slug=None): def blog_list(request, username_or_custom_slug=None):
if username_or_custom_slug: 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, snipts = Snipt.objects.filter(user=request.blog_user,
blog_post=True, blog_post=True,
public=True, public=True,
publish_date__lte=datetime.datetime.now() publish_date__lte=datetime.datetime.now()) \
).order_by('-publish_date').exclude(title__iexact='Homepage').exclude(title__iexact='Work') .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(title__in=[''])
normal_snipts = normal_snipts.exclude(tags__name__in=['tmp']) normal_snipts = normal_snipts.exclude(tags__name__in=['tmp'])
normal_snipts = normal_snipts[:3] normal_snipts = normal_snipts[:3]
sidebar = get_object_or_None(Snipt, user=request.blog_user, title='Sidebar', blog_post=True) sidebar = get_object_or_None(Snipt, user=request.blog_user,
header = get_object_or_None(Snipt, user=request.blog_user, title='Header', blog_post=True) title='Sidebar', blog_post=True)
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', lexer='css', 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 = { context = {
'blog_user': request.blog_user, 'blog_user': request.blog_user,
@ -55,26 +64,33 @@ def blog_list(request, username_or_custom_slug=None):
context, context,
context_instance=RequestContext(request)) context_instance=RequestContext(request))
def blog_post(request, username_or_custom_slug): def blog_post(request, username_or_custom_slug):
snipt = get_object_or_404(Snipt, user=request.blog_user, snipt = get_object_or_404(Snipt, user=request.blog_user,
blog_post=True, blog_post=True,
public=True, public=True,
publish_date__lte=datetime.datetime.now(), publish_date__lte=datetime.datetime.now(),
slug=username_or_custom_slug, slug=username_or_custom_slug)
)
snipts = Snipt.objects.filter(user=request.blog_user, snipts = Snipt.objects.filter(user=request.blog_user,
blog_post=True, blog_post=True,
public=True, public=True,
publish_date__lte=datetime.datetime.now() publish_date__lte=datetime.datetime.now()) \
).order_by('-publish_date').exclude(title__iexact='Homepage').exclude(title__iexact='Work') .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) sidebar = get_object_or_None(Snipt, user=request.blog_user,
header = get_object_or_None(Snipt, user=request.blog_user, title='Header', blog_post=True) title='Sidebar', blog_post=True)
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', lexer='css', 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(title__in=[''])
normal_snipts = normal_snipts.exclude(tags__name__in=['tmp']) normal_snipts = normal_snipts.exclude(tags__name__in=['tmp'])
normal_snipts = normal_snipts[:3] normal_snipts = normal_snipts[:3]
@ -100,16 +116,13 @@ def blog_post(request, username_or_custom_slug):
template = '{}/post.html'.format(template) template = '{}/post.html'.format(template)
return render_to_response( return render_to_response(template,
template, context,
context, context_instance=RequestContext(request))
context_instance=RequestContext(request)
)
def rss(request, context): def rss(request, context):
return render_to_response( return render_to_response('blogs/themes/default/rss.xml',
'blogs/themes/default/rss.xml', context,
context, context_instance=RequestContext(request),
context_instance=RequestContext(request), content_type="application/rss+xml")
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 from urlparse import urlparse
if 'DATABASE_URL' in os.environ: 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 port = es.port or 80
HAYSTACK_CONNECTIONS = { HAYSTACK_CONNECTIONS = {
@ -19,9 +21,11 @@ if 'DATABASE_URL' in os.environ:
} }
if es.username: 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 ACCOUNT_ACTIVATION_DAYS = 0
ADMINS = (('Nick Sergeant', 'nick@snipt.net'),) ADMINS = (('Nick Sergeant', 'nick@snipt.net'),)
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
@ -46,7 +50,7 @@ MEDIA_URL = '/media/uploads/'
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
POSTMARK_API_KEY = os.environ.get('POSTMARK_API_KEY', '') POSTMARK_API_KEY = os.environ.get('POSTMARK_API_KEY', '')
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) 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 REGISTRATION_EMAIL_HTML = False
ROOT_URLCONF = 'urls' ROOT_URLCONF = 'urls'
SECRET_KEY = os.environ.get('SECRET_KEY', 'changeme') 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 SESSION_COOKIE_SECURE = True if 'USE_SSL' in os.environ else False
SITE_ID = 1 SITE_ID = 1
STATICFILES_DIRS = (os.path.join(BASE_PATH, 'media'),) 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' STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
STATIC_ROOT = os.path.join(BASE_PATH, 'static') STATIC_ROOT = os.path.join(BASE_PATH, 'static')
STATIC_URL = '/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_DIRS = (os.path.join(PROJECT_PATH, 'templates'),)
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
TIME_ZONE = 'America/New_York' TIME_ZONE = 'America/New_York'
@ -93,7 +100,6 @@ INSTALLED_APPS = (
'typogrify', 'typogrify',
'accounts', 'accounts',
'blogs', 'blogs',
'jobs',
'snipts', 'snipts',
'utils', 'utils',
) )

View File

@ -2,7 +2,7 @@ CSRF_COOKIE_SECURE = False
DEBUG = True DEBUG = True
INTERCOM_SECRET_KEY = '' INTERCOM_SECRET_KEY = ''
POSTMARK_API_KEY = '' POSTMARK_API_KEY = ''
RAVEN_CONFIG = { 'dsn': '' } RAVEN_CONFIG = {'dsn': ''}
SECRET_KEY = 'changeme' SECRET_KEY = 'changeme'
SESSION_COOKIE_SECURE = False SESSION_COOKIE_SECURE = False
SSLIFY_DISABLE = False SSLIFY_DISABLE = False

View File

@ -5,7 +5,9 @@ from snipts.models import Favorite, Snipt
class SniptAdmin(admin.ModelAdmin): class SniptAdmin(admin.ModelAdmin):
readonly_fields = ('user',) 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',) list_filter = ('blog_post',)
search_fields = ('title', 'slug', 'user__username', 'lexer', 'id', 'key',) search_fields = ('title', 'slug', 'user__username', 'lexer', 'id', 'key',)
ordering = ('-created',) ordering = ('-created',)
@ -13,6 +15,7 @@ class SniptAdmin(admin.ModelAdmin):
admin.site.register(Snipt, SniptAdmin) admin.site.register(Snipt, SniptAdmin)
class FavoriteAdmin(admin.ModelAdmin): class FavoriteAdmin(admin.ModelAdmin):
readonly_fields = ('snipt', 'user',) readonly_fields = ('snipt', 'user',)
list_display = ('snipt', 'user', 'created',) list_display = ('snipt', 'user', 'created',)

View File

@ -12,12 +12,14 @@ from haystack.query import SearchQuerySet
from accounts.models import UserProfile from accounts.models import UserProfile
from tastypie.cache import SimpleCache from tastypie.cache import SimpleCache
from tastypie.fields import ListField from tastypie.fields import ListField
from django.http import HttpResponse
from taggit.models import Tag from taggit.models import Tag
from django.db import models from django.db import models
from tastypie import fields from tastypie import fields
import datetime, hashlib, time, re import datetime
import hashlib
import re
import time
import parsedatetime as pdt import parsedatetime as pdt
@ -49,6 +51,7 @@ class PrivateFavoriteAuthorization(Authorization):
def delete_detail(self, object_list, bundle): def delete_detail(self, object_list, bundle):
return bundle.obj.user == bundle.request.user return bundle.obj.user == bundle.request.user
class PrivateSniptAuthorization(Authorization): class PrivateSniptAuthorization(Authorization):
def read_list(self, object_list, bundle): def read_list(self, object_list, bundle):
return object_list.filter(user=bundle.request.user) return object_list.filter(user=bundle.request.user)
@ -74,6 +77,7 @@ class PrivateSniptAuthorization(Authorization):
def delete_detail(self, object_list, bundle): def delete_detail(self, object_list, bundle):
return bundle.obj.user == bundle.request.user return bundle.obj.user == bundle.request.user
class PrivateUserProfileAuthorization(Authorization): class PrivateUserProfileAuthorization(Authorization):
def read_list(self, object_list, bundle): def read_list(self, object_list, bundle):
raise Unauthorized() raise Unauthorized()
@ -99,6 +103,7 @@ class PrivateUserProfileAuthorization(Authorization):
def delete_detail(self, object_list, bundle): def delete_detail(self, object_list, bundle):
raise Unauthorized() raise Unauthorized()
class PrivateUserAuthorization(Authorization): class PrivateUserAuthorization(Authorization):
def read_list(self, object_list, bundle): def read_list(self, object_list, bundle):
raise Unauthorized() raise Unauthorized()
@ -130,22 +135,27 @@ class FavoriteValidation(Validation):
errors = {} errors = {}
snipt = bundle.data['snipt'] 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.' errors['duplicate'] = 'User has already favorited this snipt.'
return errors return errors
class SniptValidation(Validation): class SniptValidation(Validation):
def is_valid(self, bundle, request=None): def is_valid(self, bundle, request=None):
errors = {} errors = {}
if 'pk' not in bundle.data and \ if 'pk' not in bundle.data and \
request.user.profile.get_account_age() > 7 and \ request.user.profile.get_account_age() > 7 and \
request.user.profile.is_pro == False: 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." errors['expired'] = """Your trial has expired. You'll need
to go Pro (https://snipt.net/pro/)
in order to create new snipts."""
return errors return errors
class UserProfileValidation(Validation): class UserProfileValidation(Validation):
def is_valid(self, bundle, request=None): def is_valid(self, bundle, request=None):
errors = {} errors = {}
@ -153,7 +163,9 @@ class UserProfileValidation(Validation):
for field in bundle.data: for field in bundle.data:
if bundle.data[field]: if bundle.data[field]:
if not re.match('^[ A-Za-z0-9\/\@\._-]*$', 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 return errors
@ -162,26 +174,31 @@ class PublicUserResource(ModelResource):
class Meta: class Meta:
queryset = User.objects.all() queryset = User.objects.all()
resource_name = 'user' resource_name = 'user'
fields = ['id', 'username',] fields = ['id', 'username']
include_absolute_url = True include_absolute_url = True
allowed_methods = ['get'] allowed_methods = ['get']
filtering = { 'username': 'exact', } filtering = {'username': 'exact'}
max_limit = 200 max_limit = 200
cache = SimpleCache() cache = SimpleCache()
def dehydrate(self, bundle): def dehydrate(self, bundle):
bundle.data['snipts'] = '/api/public/snipt/?user=%d' % bundle.obj.id bundle.data['snipts'] = '/api/public/snipt/?user=%d' % bundle.obj.id
bundle.data['email_md5'] = hashlib.md5(bundle.obj.email.lower()).hexdigest() bundle.data['email_md5'] = hashlib \
bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id, public=True).count() .md5(bundle.obj.email.lower()) \
.hexdigest()
bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id,
public=True).count()
return bundle return bundle
class PublicTagResource(ModelResource): class PublicTagResource(ModelResource):
class Meta: class Meta:
queryset = Tag.objects.filter() 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') queryset = queryset.order_by('-count', 'name')
resource_name = 'tag' resource_name = 'tag'
fields = ['id', 'name',] fields = ['id', 'name']
allowed_methods = ['get'] allowed_methods = ['get']
max_limit = 200 max_limit = 200
cache = SimpleCache() cache = SimpleCache()
@ -202,19 +219,22 @@ class PublicTagResource(ModelResource):
bundle.data['snipts'] = '/api/public/snipt/?tag=%d' % bundle.obj.id bundle.data['snipts'] = '/api/public/snipt/?tag=%d' % bundle.obj.id
return bundle return bundle
class PublicSniptResource(ModelResource): class PublicSniptResource(ModelResource):
user = fields.ForeignKey(PublicUserResource, 'user', full=True) 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: class Meta:
queryset = Snipt.objects.filter(public=True).order_by('-created') queryset = Snipt.objects.filter(public=True).order_by('-created')
resource_name = 'snipt' resource_name = 'snipt'
fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', 'line_count', fields = ['id', 'title', 'slug', 'lexer', 'code', 'description',
'stylized', 'created', 'modified', 'publish_date', 'blog_post', 'meta',] 'line_count', 'stylized', 'created', 'modified',
'publish_date', 'blog_post', 'meta']
include_absolute_url = True include_absolute_url = True
allowed_methods = ['get'] allowed_methods = ['get']
filtering = { 'user': 'exact', 'blog_post': 'exact' } filtering = {'user': 'exact', 'blog_post': 'exact'}
ordering = ['created', 'modified',] ordering = ['created', 'modified']
max_limit = 200 max_limit = 200
cache = SimpleCache() cache = SimpleCache()
@ -222,7 +242,8 @@ class PublicSniptResource(ModelResource):
bundle.data['embed_url'] = bundle.obj.get_embed_url() bundle.data['embed_url'] = bundle.obj.get_embed_url()
bundle.data['raw_url'] = bundle.obj.get_raw_url() bundle.data['raw_url'] = bundle.obj.get_raw_url()
bundle.data['full_absolute_url'] = bundle.obj.get_full_absolute_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: if 'omit_code' in bundle.request.GET:
del bundle.data['code'] del bundle.data['code']
@ -272,8 +293,10 @@ class PrivateUserProfileResource(ModelResource):
bundle.data['is_pro'] = bundle.obj.user.profile.is_pro bundle.data['is_pro'] = bundle.obj.user.profile.is_pro
return bundle return bundle
class PrivateUserResource(ModelResource): class PrivateUserResource(ModelResource):
profile = fields.ForeignKey(PrivateUserProfileResource, 'profile', full=False) profile = fields.ForeignKey(PrivateUserProfileResource, 'profile',
full=False)
class Meta: class Meta:
queryset = User.objects.all() queryset = User.objects.all()
@ -289,18 +312,25 @@ class PrivateUserResource(ModelResource):
cache = SimpleCache() cache = SimpleCache()
def dehydrate(self, bundle): 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['is_pro'] = bundle.obj.profile.is_pro
bundle.data['stats'] = { bundle.data['stats'] = {
'public_snipts': Snipt.objects.filter(user=bundle.obj.id, public=True).count(), 'public_snipts': Snipt.objects.filter(user=bundle.obj.id,
'private_snipts': Snipt.objects.filter(user=bundle.obj.id, public=False).count(), 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_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 \ bundle.data['lexers'] = [
Snipt.objects.filter(user=bundle.obj).values('lexer').distinct()] snipt['lexer'] for snipt in Snipt.objects.filter(user=bundle.obj)
.values('lexer').distinct()]
return bundle return bundle
class PrivateSniptResource(ModelResource): class PrivateSniptResource(ModelResource):
user = fields.ForeignKey(PrivateUserResource, 'user', full=True) user = fields.ForeignKey(PrivateUserResource, 'user', full=True)
tags_list = ListField() tags_list = ListField()
@ -308,15 +338,16 @@ class PrivateSniptResource(ModelResource):
class Meta: class Meta:
queryset = Snipt.objects.all().order_by('-created') queryset = Snipt.objects.all().order_by('-created')
resource_name = 'snipt' resource_name = 'snipt'
fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', 'line_count', 'stylized', fields = ['id', 'title', 'slug', 'lexer', 'code', 'description',
'key', 'public', 'blog_post', 'created', 'modified', 'publish_date', 'meta',] 'line_count', 'stylized', 'key', 'public', 'blog_post',
'created', 'modified', 'publish_date', 'meta']
include_absolute_url = True include_absolute_url = True
detail_allowed_methods = ['get', 'patch', 'put', 'delete'] detail_allowed_methods = ['get', 'patch', 'put', 'delete']
list_allowed_methods = ['get', 'post'] list_allowed_methods = ['get', 'post']
authentication = ApiKeyAuthentication() authentication = ApiKeyAuthentication()
authorization = PrivateSniptAuthorization() authorization = PrivateSniptAuthorization()
validation = SniptValidation() validation = SniptValidation()
ordering = ['created', 'modified',] ordering = ['created', 'modified']
always_return_data = True always_return_data = True
max_limit = 200 max_limit = 200
cache = SimpleCache() cache = SimpleCache()
@ -326,12 +357,14 @@ class PrivateSniptResource(ModelResource):
bundle.data['raw_url'] = bundle.obj.get_raw_url() bundle.data['raw_url'] = bundle.obj.get_raw_url()
bundle.data['tags_list'] = edit_string_for_tags(bundle.obj.tags.all()) 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['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['views'] = bundle.obj.views
bundle.data['favs'] = bundle.obj.favs() bundle.data['favs'] = bundle.obj.favs()
if bundle.data['publish_date']: 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 return bundle
@ -342,8 +375,9 @@ class PrivateSniptResource(ModelResource):
if 'blog_post' in bundle.data: if 'blog_post' in bundle.data:
bundle = self._clean_publish_date(bundle) bundle = self._clean_publish_date(bundle)
return super(PrivateSniptResource, self).obj_create(bundle, return super(PrivateSniptResource, self) \
user=bundle.request.user, **kwargs) .obj_create(bundle,
user=bundle.request.user, **kwargs)
def obj_update(self, bundle, **kwargs): def obj_update(self, bundle, **kwargs):
bundle.data['user'] = bundle.request.user bundle.data['user'] = bundle.request.user
@ -359,8 +393,9 @@ class PrivateSniptResource(ModelResource):
if 'blog_post' in bundle.data: if 'blog_post' in bundle.data:
bundle = self._clean_publish_date(bundle) bundle = self._clean_publish_date(bundle)
return super(PrivateSniptResource, self).obj_update(bundle, return super(PrivateSniptResource, self) \
user=bundle.request.user, **kwargs) .obj_update(bundle,
user=bundle.request.user, **kwargs)
def _clean_publish_date(self, bundle): def _clean_publish_date(self, bundle):
if bundle.data['blog_post'] and 'publish_date' not in bundle.data: if bundle.data['blog_post'] and 'publish_date' not in bundle.data:
@ -407,6 +442,7 @@ class PrivateSniptResource(ModelResource):
else: else:
bundle.obj.tags.set() bundle.obj.tags.set()
class PrivateFavoriteResource(ModelResource): class PrivateFavoriteResource(ModelResource):
user = fields.ForeignKey(PrivateUserResource, 'user', full=True) user = fields.ForeignKey(PrivateUserResource, 'user', full=True)
snipt = fields.ForeignKey(PrivateSniptResource, 'snipt', full=False) snipt = fields.ForeignKey(PrivateSniptResource, 'snipt', full=False)
@ -414,24 +450,24 @@ class PrivateFavoriteResource(ModelResource):
class Meta: class Meta:
queryset = Favorite.objects.all().order_by('-created') queryset = Favorite.objects.all().order_by('-created')
resource_name = 'favorite' resource_name = 'favorite'
fields = ['id',] fields = ['id']
validation = FavoriteValidation() validation = FavoriteValidation()
detail_allowed_methods = ['get', 'post', 'delete'] detail_allowed_methods = ['get', 'post', 'delete']
list_allowed_methods = ['get', 'post',] list_allowed_methods = ['get', 'post']
authentication = ApiKeyAuthentication() authentication = ApiKeyAuthentication()
authorization = PrivateFavoriteAuthorization() authorization = PrivateFavoriteAuthorization()
ordering = ['created',] ordering = ['created']
always_return_data = True always_return_data = True
max_limit = 200 max_limit = 200
cache = SimpleCache() cache = SimpleCache()
def dehydrate(self, bundle): def dehydrate(self, bundle):
bundle.data['snipt'] = '/api/public/snipt/{}/'.format( bundle.data['snipt'] = '/api/public/snipt/{}/'.format(
bundle.obj.snipt.pk) bundle.obj.snipt.pk)
return bundle return bundle
def obj_create(self, bundle, **kwargs): def obj_create(self, bundle, **kwargs):
bundle.data['user'] = bundle.request.user bundle.data['user'] = bundle.request.user
bundle.data['snipt'] = Snipt.objects.get(pk=bundle.data['snipt']) bundle.data['snipt'] = Snipt.objects.get(pk=bundle.data['snipt'])
return super(PrivateFavoriteResource, self).obj_create(bundle, return super(PrivateFavoriteResource, self) \
user=bundle.request.user, **kwargs) .obj_create(bundle, user=bundle.request.user, **kwargs)

View File

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

View File

@ -15,36 +15,40 @@ from pygments.formatters import HtmlFormatter
from snipts.utils import slugify_uniquely from snipts.utils import slugify_uniquely
import datetime, hashlib, random, re import datetime
import hashlib
import random
import re
class Snipt(models.Model): class Snipt(models.Model):
"""An individual Snipt.""" """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') title = models.CharField(max_length=255, blank=True, null=True,
slug = models.SlugField(max_length=255, blank=True) default='Untitled')
custom_slug = models.SlugField(max_length=255, blank=True) slug = models.SlugField(max_length=255, blank=True)
tags = TaggableManager() custom_slug = models.SlugField(max_length=255, blank=True)
tags = TaggableManager()
lexer = models.CharField(max_length=50) lexer = models.CharField(max_length=50)
code = models.TextField() code = models.TextField()
meta = models.TextField(blank=True, null=True) meta = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True) description = models.TextField(blank=True, null=True)
stylized = models.TextField(blank=True, null=True) stylized = models.TextField(blank=True, null=True)
stylized_min = models.TextField(blank=True, null=True) stylized_min = models.TextField(blank=True, null=True)
embedded = models.TextField(blank=True, null=True) embedded = models.TextField(blank=True, null=True)
line_count = models.IntegerField(blank=True, null=True, default=None) line_count = models.IntegerField(blank=True, null=True, default=None)
key = models.CharField(max_length=100, blank=True, null=True) key = models.CharField(max_length=100, blank=True, null=True)
public = models.BooleanField(default=False) public = models.BooleanField(default=False)
blog_post = models.BooleanField(default=False) blog_post = models.BooleanField(default=False)
views = models.IntegerField(default=0) views = models.IntegerField(default=0)
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) modified = models.DateTimeField(auto_now=True, editable=False)
publish_date = models.DateTimeField(blank=True, null=True) publish_date = models.DateTimeField(blank=True, null=True)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -53,7 +57,9 @@ class Snipt(models.Model):
self.slug = slugify_uniquely(self.title, Snipt) self.slug = slugify_uniquely(self.title, Snipt)
if not self.key: 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': if self.lexer == 'markdown':
self.stylized = markdown(self.code, 'default') self.stylized = markdown(self.code, 'default')
@ -61,21 +67,46 @@ class Snipt(models.Model):
# Snipt embeds # Snipt embeds
for match in re.findall('\[\[(\w{32})\]\]', self.stylized): for match in re.findall('\[\[(\w{32})\]\]', self.stylized):
self.stylized = self.stylized.replace('[[' + str(match) + ']]', 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 # YouTube embeds
for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]', self.stylized): for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]',
self.stylized = self.stylized.replace('[[youtube-{}-{}x{}]]'.format(str(match[0]), str(match[1]), str(match[2])), self.stylized):
'<iframe width="{}" height="{}" src="https://www.youtube.com/embed/{}" frameborder="0" allowfullscreen></iframe>'.format(match[1], match[2], match[0])) 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 # Vimeo embeds
for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]', self.stylized): for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]',
self.stylized = self.stylized.replace('[[vimeo-{}-{}x{}]]'.format(str(match[0]), str(match[1]), str(match[2])), self.stylized):
'<iframe src="https://player.vimeo.com/video/{}" width="{}" height="{}" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'.format(match[0], match[1], match[2])) 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 # Tweet embeds
for match in re.findall('\[\[tweet-(\d+)\]\]', self.stylized): 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 # Parse Snipt usernames
for match in re.findall('@(\w+) ', self.stylized): for match in re.findall('@(\w+) ', self.stylized):
@ -85,17 +116,19 @@ class Snipt(models.Model):
if user: if user:
url = user.profile.get_user_profile_url() 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: else:
self.stylized = highlight(self.code, 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', HtmlFormatter(linenos='table',
anchorlinenos=True, anchorlinenos=True,
lineanchors='L', lineanchors='L',
linespans='L', linespans='L',
) ))
)
self.line_count = len(self.code.split('\n')) self.line_count = len(self.code.split('\n'))
if self.lexer == 'markdown': if self.lexer == 'markdown':
@ -104,7 +137,8 @@ class Snipt(models.Model):
lexer_for_embedded = self.lexer lexer_for_embedded = self.lexer
embedded = highlight(self.code, 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( HtmlFormatter(
style='native', style='native',
noclasses=True, noclasses=True,
@ -120,8 +154,8 @@ class Snipt(models.Model):
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
-moz-border-radius: 5px; -moz-border-radius: 5px;
""")) """))
embedded = (embedded.replace("\\\"","\\\\\"") embedded = (embedded.replace("\\\"", "\\\\\"")
.replace('\'','\\\'') .replace('\'', '\\\'')
.replace("\\", "\\\\") .replace("\\", "\\\\")
.replace('background: #202020', '')) .replace('background: #202020', ''))
self.embedded = embedded self.embedded = embedded
@ -139,18 +173,23 @@ class Snipt(models.Model):
if self.lexer == 'markdown': if self.lexer == 'markdown':
self.stylized_min = markdown(self.code[:1000], 'default') self.stylized_min = markdown(self.code[:1000], 'default')
else: else:
self.stylized_min = highlight(self.code[:1000], self.stylized_min = highlight(
get_lexer_by_name(self.lexer, encoding='UTF-8'), self.code[:1000],
HtmlFormatter(linenos='table', linenospecial=1, lineanchors='line')) get_lexer_by_name(self.lexer, encoding='UTF-8'),
HtmlFormatter(linenos='table',
linenospecial=1,
lineanchors='line'))
return self.stylized_min return self.stylized_min
def get_absolute_url(self): def get_absolute_url(self):
if self.blog_post: if self.blog_post:
if self.user.profile.blog_domain: 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: 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: if self.custom_slug:
return u'/{}/'.format(self.custom_slug) return u'/{}/'.format(self.custom_slug)
@ -158,15 +197,18 @@ class Snipt(models.Model):
if self.public: if self.public:
return u'/{}/{}/'.format(self.user.username, self.slug) return u'/{}/{}/'.format(self.user.username, self.slug)
else: 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): def get_full_absolute_url(self):
if self.blog_post: if self.blog_post:
if self.user.profile.blog_domain: 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: 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: if settings.DEBUG:
root = 'http://local.snipt.net' root = 'http://local.snipt.net'
@ -176,7 +218,10 @@ class Snipt(models.Model):
if self.public: if self.public:
return u'{}/{}/{}/'.format(root, self.user.username, self.slug) return u'{}/{}/{}/'.format(root, self.user.username, self.slug)
else: 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): def get_download_url(self):
@ -233,12 +278,14 @@ class Snipt(models.Model):
else: else:
return get_lexer_by_name(self.lexer).name return get_lexer_by_name(self.lexer).name
class Favorite(models.Model): class Favorite(models.Model):
snipt = models.ForeignKey(Snipt) 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) modified = models.DateTimeField(auto_now=True, editable=False)
def __unicode__(self): 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): def index_queryset(self, **kwargs):
"""Used when the entire index for model is updated.""" """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() register = template.Library()
@tag(register, [Constant('as'), Variable()]) @tag(register, [Constant('as'), Variable()])
def snipt_is_favorited_by_user(context, asvar): def snipt_is_favorited_by_user(context, asvar):
@ -30,6 +31,7 @@ def snipt_is_favorited_by_user(context, asvar):
return '' return ''
@tag(register, []) @tag(register, [])
def snipts_count_for_user(context): def snipts_count_for_user(context):
@ -42,11 +44,13 @@ def snipts_count_for_user(context):
return snipts return snipts
@tag(register, [Constant('as'), Variable()]) @tag(register, [Constant('as'), Variable()])
def get_lexers(context, asvar): def get_lexers(context, asvar):
context[asvar] = get_lexers_list() context[asvar] = get_lexers_list()
return '' return ''
@tag(register, [Constant('for'), Variable()]) @tag(register, [Constant('for'), Variable()])
def generate_line_numbers(context, line_numbers): def generate_line_numbers(context, line_numbers):
html = '' html = ''
@ -56,6 +60,7 @@ def generate_line_numbers(context, line_numbers):
return html return html
@register.filter @register.filter
def md5(string): def md5(string):
return hashlib.md5(string.lower()).hexdigest() return hashlib.md5(string.lower()).hexdigest()

View File

@ -11,11 +11,19 @@ class SniptResourceTest(ResourceTestCase):
super(SniptResourceTest, self).setUp() super(SniptResourceTest, self).setUp()
# Johnny # 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) ApiKey.objects.get_or_create(user=self.johnny)
self.johnny_auth = self.create_apikey(self.johnny, self.johnny.api_key.key) self.johnny_auth = self.create_apikey(self.johnny,
self.johnny_private = Snipt(title='Private snipt for Johnny', lexer='text', public=False, user=self.johnny) self.johnny.api_key.key)
self.johnny_public = Snipt(title='Public snipt for Johnny', lexer='text', public=True, user=self.johnny) 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_private.save()
self.johnny_public.save() self.johnny_public.save()
@ -23,16 +31,21 @@ class SniptResourceTest(ResourceTestCase):
self.bob = User.objects.create_user('bob', 'bob@snipt.net', 'password') self.bob = User.objects.create_user('bob', 'bob@snipt.net', 'password')
ApiKey.objects.get_or_create(user=self.bob) ApiKey.objects.get_or_create(user=self.bob)
self.bob_auth = self.create_apikey(self.bob, self.bob.api_key.key) 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_private = Snipt(title='Private snipt for Bob',
self.bob_public = Snipt(title='Public snipt for Bob', lexer='text', public=True, user=self.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_private.save()
self.bob_public.save() self.bob_public.save()
# Private
def test_get_private_list(self): 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.assertHttpOK(resp)
self.assertValidJSONResponse(resp) self.assertValidJSONResponse(resp)
@ -40,18 +53,27 @@ class SniptResourceTest(ResourceTestCase):
def test_get_private_detail(self): 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.assertHttpOK(resp)
self.assertValidJSONResponse(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. # 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) self.assertHttpUnauthorized(resp)
# Unauthorized request. # 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) self.assertHttpUnauthorized(resp)
def test_post_private_list(self): def test_post_private_list(self):
@ -63,21 +85,21 @@ class SniptResourceTest(ResourceTestCase):
} }
resp = self.api_client.post('/api/private/snipt/', resp = self.api_client.post('/api/private/snipt/',
data=new_snipt, data=new_snipt,
format='json', format='json',
authentication=self.johnny_auth) authentication=self.johnny_auth)
self.assertHttpCreated(resp) self.assertHttpCreated(resp)
self.assertEqual(Snipt.objects.count(), 5) 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) self.assertEqual(len(self.deserialize(resp)['objects']), 3)
resp = self.api_client.get('/api/public/snipt/', format='json') resp = self.api_client.get('/api/public/snipt/', format='json')
self.assertEqual(len(self.deserialize(resp)['objects']), 2) self.assertEqual(len(self.deserialize(resp)['objects']), 2)
# Public
def test_get_public_list(self): def test_get_public_list(self):
self.assertEqual(Snipt.objects.count(), 4) self.assertEqual(Snipt.objects.count(), 4)

View File

@ -3,23 +3,46 @@ from django.conf.urls import *
from snipts import views from snipts import views
urlpatterns = patterns('', urlpatterns = \
patterns('',
# Redirects url(r'^s/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$',
url(r'^s/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', views.redirect_snipt, name='redirect-snipt'), views.redirect_snipt, name='redirect-snipt'),
url(r'^(?P<username>[^/]+)/feed/$', views.redirect_user_feed, name='redirect-feed'), url(r'^(?P<username>[^/]+)/feed/$',
url(r'^public/tag/(?P<tag_slug>[^/]+)/feed/$', views.redirect_public_tag_feed, name='redirect-public-tag-feed'), views.redirect_user_feed,
url(r'^(?P<username>[^/]+)/tag/(?P<tag_slug>[^/]+)/feed/$', views.redirect_user_tag_feed, name='redirect-user-tag-feed'), name='redirect-feed'),
url(r'^public/tag/(?P<tag_slug>[^/]+)/feed/$',
url(r'^public/$', views.list_public, name='list-public'), views.redirect_public_tag_feed,
url(r'^public/tag/(?P<tag_slug>[^/]+)/$', views.list_public, name='list-public-tag'), name='redirect-public-tag-feed'),
url(r'^download/(?P<snipt_key>[^/]+).*$', views.download, name='download'), url(r'^(?P<username>[^/]+)/tag/(?P<tag_slug>[^/]+)/feed/$',
url(r'^embed/(?P<snipt_key>[^/]+)/$', views.embed, name='embed'), views.redirect_user_tag_feed,
url(r'^raw/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', views.raw, name='raw'), name='redirect-user-tag-feed'),
url(r'^(?P<username_or_custom_slug>[^/]+)/$', views.list_user, name='list-user'), url(r'^public/$',
url(r'^(?P<username_or_custom_slug>[^/]+)/tag/(?P<tag_slug>[^/]+)/$', views.list_user, name='list-user-tag'), views.list_public,
url(r'^(?P<username>[^/]+)/favorites/$', views.favorites, name='favorites'), name='list-public'),
url(r'^(?P<username>[^/]+)/blog-posts/$', views.blog_posts, name='blog-posts'), url(r'^public/tag/(?P<tag_slug>[^/]+)/$',
url(r'^(?P<username>[^/]+)/(?P<snipt_slug>[^/]+)/$', views.detail, name='detail'), 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 return potential
suffix = str(uuid.uuid4()).split('-')[0] suffix = str(uuid.uuid4()).split('-')[0]
def activate_user(user, request, **kwargs): def activate_user(user, request, **kwargs):
user.is_active = True user.is_active = True
user.save() user.save()
@ -25,6 +26,7 @@ def activate_user(user, request, **kwargs):
password=request.POST['password1']) password=request.POST['password1'])
login(request, user) login(request, user)
def get_lexers_list(): def get_lexers_list():
lexers = list(get_all_lexers()) lexers = list(get_all_lexers())

View File

@ -62,10 +62,12 @@ def detail(request, username, snipt_slug):
'user': user, 'user': user,
} }
def download(request, snipt_key): def download(request, snipt_key):
snipt = get_object_or_404(Snipt, key=snipt_key) snipt = get_object_or_404(Snipt, key=snipt_key)
return HttpResponse(snipt.code, content_type='application/x-download') return HttpResponse(snipt.code, content_type='application/x-download')
def embed(request, snipt_key): def embed(request, snipt_key):
snipt = get_object_or_404(Snipt, key=snipt_key) snipt = get_object_or_404(Snipt, key=snipt_key)
@ -75,6 +77,7 @@ def embed(request, snipt_key):
context_instance=RequestContext(request), context_instance=RequestContext(request),
content_type='application/javascript') content_type='application/javascript')
@render_to('snipts/list-user.html') @render_to('snipts/list-user.html')
def blog_posts(request, username): def blog_posts(request, username):
@ -92,7 +95,8 @@ def blog_posts(request, username):
public_user = True public_user = True
user = get_object_or_404(User, username=username) user = get_object_or_404(User, username=username)
snipts = Snipt.objects.filter(blog_post=True, user=user, public=True) 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') tags = tags.order_by('name')
snipts = snipts.order_by('-created') snipts = snipts.order_by('-created')
@ -112,6 +116,7 @@ def blog_posts(request, username):
return context return context
@render_to('snipts/list-user.html') @render_to('snipts/list-user.html')
def favorites(request, username): def favorites(request, username):
@ -148,6 +153,7 @@ def favorites(request, username):
return context return context
@render_to('snipts/list-public.html') @render_to('snipts/list-public.html')
def list_public(request, tag_slug=None): def list_public(request, tag_slug=None):
@ -175,6 +181,7 @@ def list_public(request, tag_slug=None):
return context return context
@render_to('snipts/list-user.html') @render_to('snipts/list-user.html')
def list_user(request, username_or_custom_slug, tag_slug=None): 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 tags = Tag.objects
snipts = Snipt.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 public = False
favorites = Favorite.objects.filter(user=user).values('snipt') 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 return context
def raw(request, snipt_key, lexer=None): def raw(request, snipt_key, lexer=None):
snipt = get_object_or_404(Snipt, key=snipt_key) snipt = get_object_or_404(Snipt, key=snipt_key)
@ -250,23 +259,23 @@ def raw(request, snipt_key, lexer=None):
snipt.lexer = lexer snipt.lexer = lexer
snipt.save() snipt.save()
content_type='text/plain' content_type = 'text/plain'
if 'nice' in request.GET: if 'nice' in request.GET:
content_type='text/html' content_type = 'text/html'
return render_to_response('snipts/raw.html', return render_to_response('snipts/raw.html',
{'snipt': snipt}, {'snipt': snipt},
context_instance=RequestContext(request), context_instance=RequestContext(request),
content_type=content_type) content_type=content_type)
def rss(request, context): def rss(request, context):
return render_to_response( return render_to_response('rss.xml',
'rss.xml', context,
context, context_instance=RequestContext(request),
context_instance=RequestContext(request), content_type="application/rss+xml")
content_type="application/rss+xml"
)
def search(request, template='search/search.html', load_all=True, def search(request, template='search/search.html', load_all=True,
form_class=ModelSearchForm, searchqueryset=None, form_class=ModelSearchForm, searchqueryset=None,
@ -279,12 +288,18 @@ def search(request, template='search/search.html', load_all=True,
# We have a query. # We have a query.
if request.GET.get('q'): if request.GET.get('q'):
if request.user.is_authenticated() and '--mine' in request.GET.get('q'): if request.user.is_authenticated() and '--mine' in \
searchqueryset = SearchQuerySet().filter(author=request.user).order_by('-pub_date') request.GET.get('q'):
searchqueryset = SearchQuerySet().filter(author=request.user) \
.order_by('-pub_date')
else: 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(): if form.is_valid():
query = form.cleaned_data['q'] query = form.cleaned_data['q']
@ -314,19 +329,24 @@ def search(request, template='search/search.html', load_all=True,
if extra_context: if extra_context:
context.update(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): def redirect_snipt(request, snipt_key, lexer=None):
snipt = get_object_or_404(Snipt, key=snipt_key) snipt = get_object_or_404(Snipt, key=snipt_key)
return HttpResponseRedirect(snipt.get_absolute_url()) return HttpResponseRedirect(snipt.get_absolute_url())
def redirect_public_tag_feed(request, tag_slug): def redirect_public_tag_feed(request, tag_slug):
return HttpResponseRedirect('/public/tag/{}/?rss'.format(tag_slug)) return HttpResponseRedirect('/public/tag/{}/?rss'.format(tag_slug))
def redirect_user_feed(request, username): def redirect_user_feed(request, username):
user = get_object_or_404(User, username=username) user = get_object_or_404(User, username=username)
return HttpResponseRedirect(user.get_absolute_url() + '?rss') return HttpResponseRedirect(user.get_absolute_url() + '?rss')
def redirect_user_tag_feed(request, username, tag_slug): def redirect_user_tag_feed(request, username, tag_slug):
return HttpResponseRedirect(u'/{}/tag/{}/?rss'.format(username, tag_slug)) return HttpResponseRedirect(u'/{}/tag/{}/?rss'.format(username, tag_slug))

View File

@ -66,7 +66,6 @@
{% endif %} {% endif %}
{% else %} {% else %}
<script> <script>
document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')
window.ll = function() {}; window.ll = function() {};
</script> </script>
{% endif %} {% 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 import include, patterns, url
from django.conf.urls.static import static
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib import admin from django.contrib import admin
@ -11,11 +9,9 @@ from snipts.api import (PublicSniptResource,
from snipts.views import search from snipts.views import search
from tastypie.api import Api from tastypie.api import Api
from utils.views import SniptRegistrationView from utils.views import SniptRegistrationView
from jobs.views import jobs, jobs_json
from views import (homepage, lexers, login_redirect, pro, sitemap, tags, from views import (homepage, lexers, login_redirect, pro, sitemap, tags,
pro_complete, user_api_key, for_teams, for_teams_complete) pro_complete, user_api_key, for_teams, for_teams_complete)
import admin as custom_admin
import os import os
@ -32,47 +28,56 @@ private_api.register(PrivateUserResource())
private_api.register(PrivateFavoriteResource()) private_api.register(PrivateFavoriteResource())
private_api.register(PrivateUserProfileResource()) private_api.register(PrivateUserProfileResource())
urlpatterns = patterns('', urlpatterns = \
patterns('',
url(r'^$', homepage), url(r'^$', homepage),
url(r'^login-redirect/$', login_redirect), 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'^404/$', TemplateView.as_view(template_name='404.html')),
url(r'^500/$', TemplateView.as_view(template_name='500.html')), url(r'^500/$', TemplateView.as_view(template_name='500.html')),
url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')), url(r'^robots.txt$',
url(r'^humans.txt$', TemplateView.as_view(template_name='humans.txt')), TemplateView.as_view(template_name='robots.txt')),
url(r'^sitemap.xml$', sitemap), url(r'^humans.txt$',
url(r'^tags/$', tags), TemplateView.as_view(template_name='humans.txt')),
url(r'^sitemap.xml$', sitemap),
url(r'^tags/$', tags),
url(r'^pro/$', pro), url(r'^pro/$', pro),
url(r'^pro/complete/$', pro_complete), url(r'^pro/complete/$', pro_complete),
url(r'^for-teams/$', for_teams), url(r'^for-teams/$', for_teams),
url(r'^for-teams/complete/$', for_teams_complete), 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/private/key/$', user_api_key),
url(r'^api/', include(public_api.urls)), url(r'^api/', include(public_api.urls)),
url(r'^api/', include(private_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'^register/$', lambda x: HttpResponseRedirect('/signup/')),
url(r'^signup/$', SniptRegistrationView.as_view(), url(r'^signup/$', SniptRegistrationView.as_view(),
name='registration_register'), name='registration_register'),
url(r'', include('registration.backends.default.urls')), 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('', urlpatterns += \
(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': os.path.join(os.path.dirname(__file__), 'media')}), 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 from django.contrib.auth.models import User
class EmailOrUsernameModelBackend(object): class EmailOrUsernameModelBackend(object):
def authenticate(self, username=None, password=None): def authenticate(self, username=None, password=None):
if '@' in username: if '@' in username:

View File

@ -3,21 +3,22 @@ from registration.forms import RegistrationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django import forms from django import forms
class SniptRegistrationForm(RegistrationForm): class SniptRegistrationForm(RegistrationForm):
""" """
Subclass of ``RegistrationForm`` which enforces uniqueness of Subclass of ``RegistrationForm`` which enforces uniqueness of
email addresses and further restricts usernames. email addresses and further restricts usernames.
""" """
def clean_username(self): def clean_username(self):
""" """
Validate that the username is alphanumeric and is not already Validate that the username is alphanumeric and is not already
in use. 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(): 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']: elif '@' in self.cleaned_data['username']:
raise forms.ValidationError(_("Cannot have '@' in 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 Validate that the supplied email address is unique for the
site. site.
""" """
if User.objects.filter(email__iexact=self.cleaned_data['email']): 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'] return self.cleaned_data['email']

View File

@ -1,10 +1,16 @@
from django.conf import settings from django.conf import settings
from django import template from django import template
import hmac, hashlib, os import hmac
import hashlib
import os
register = template.Library() register = template.Library()
@register.filter @register.filter
def intercom_sha_256(user_id): 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): def __init__(self, text):
self.text = text self.text = text
def render(self, context): def render(self, context):
return self.text return self.text

View File

@ -1,9 +1,9 @@
from registration.backends.default.views import RegistrationView from registration.backends.default.views import RegistrationView
from utils.forms import SniptRegistrationForm from utils.forms import SniptRegistrationForm
class SniptRegistrationView(RegistrationView): class SniptRegistrationView(RegistrationView):
""" """
Custom registration view that uses our custom form. Custom registration view that uses our custom form.
""" """
form_class = SniptRegistrationForm form_class = SniptRegistrationForm

View File

@ -1,10 +1,9 @@
from accounts.models import UserProfile 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 blogs.views import blog_list
from django.http import HttpResponseRedirect, HttpResponseBadRequest from django.http import HttpResponseRedirect, HttpResponseBadRequest
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required 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.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from snipts.utils import get_lexers_list from snipts.utils import get_lexers_list
@ -19,6 +18,7 @@ import hashlib
import os import os
import stripe import stripe
@render_to('for-teams.html') @render_to('for-teams.html')
def for_teams(request): def for_teams(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
@ -27,6 +27,7 @@ def for_teams(request):
profile.save() profile.save()
return {} return {}
@render_to('for-teams-complete.html') @render_to('for-teams-complete.html')
def for_teams_complete(request): def for_teams_complete(request):
@ -43,7 +44,8 @@ def for_teams_complete(request):
Info: Info:
%s %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) ['nick@nicksergeant.com'], fail_silently=False)
profile = request.user.profile profile = request.user.profile
@ -70,6 +72,7 @@ def for_teams_complete(request):
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
@render_to('homepage.html') @render_to('homepage.html')
def homepage(request): def homepage(request):
@ -99,6 +102,7 @@ def homepage(request):
'users_count': User.objects.all().count(), 'users_count': User.objects.all().count(),
} }
@ajax_request @ajax_request
def lexers(request): def lexers(request):
lexers = get_lexers_list() lexers = get_lexers_list()
@ -125,12 +129,14 @@ def lexers(request):
return {'objects': objects} return {'objects': objects}
def login_redirect(request): def login_redirect(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
return HttpResponseRedirect('/' + request.user.username + '/') return HttpResponseRedirect('/' + request.user.username + '/')
else: else:
return HttpResponseRedirect('/') return HttpResponseRedirect('/')
@login_required @login_required
@render_to('pro.html') @render_to('pro.html')
def pro(request): def pro(request):
@ -138,6 +144,7 @@ def pro(request):
return HttpResponseRedirect('/' + request.user.username + '/') return HttpResponseRedirect('/' + request.user.username + '/')
return {} return {}
@login_required @login_required
@render_to('pro-complete.html') @render_to('pro-complete.html')
def pro_complete(request): def pro_complete(request):
@ -145,7 +152,8 @@ def pro_complete(request):
if request.method == 'POST': if request.method == 'POST':
token = request.POST['token'] 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: if 'plan' in request.GET:
plan = request.GET['plan'] plan = request.GET['plan']
@ -158,7 +166,8 @@ def pro_complete(request):
email=request.user.email) email=request.user.email)
except stripe.CardError, e: except stripe.CardError, e:
error_message = e.json_body['error']['message'] 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 = request.user.profile
profile.is_pro = True profile.is_pro = True
@ -171,6 +180,7 @@ def pro_complete(request):
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
def sitemap(request): def sitemap(request):
tags = Tag.objects.filter(snipt__public=True) tags = Tag.objects.filter(snipt__public=True)
@ -182,6 +192,7 @@ def sitemap(request):
context_instance=RequestContext(request), context_instance=RequestContext(request),
content_type='application/xml') content_type='application/xml')
@render_to('tags.html') @render_to('tags.html')
def tags(request): def tags(request):
@ -198,13 +209,14 @@ def tags(request):
'all_tags': all_tags, 'all_tags': all_tags,
'tags': popular_tags 'tags': popular_tags
} }
@ajax_request @ajax_request
def user_api_key(request): def user_api_key(request):
if not request.user.is_authenticated(): if not request.user.is_authenticated():
return HttpResponseBadRequest() return HttpResponseBadRequest()
return {
'api_key': request.user.api_key.key
}
return {
'api_key': request.user.api_key.key
}