master
Nick Sergeant 2019-01-23 18:52:55 -05:00
parent 6af387be82
commit 39403a600c
49 changed files with 1464 additions and 1169 deletions

View File

@ -3,9 +3,9 @@ from django.contrib import admin
class UserProfileAdmin(admin.ModelAdmin): class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'is_pro', 'stripe_id', 'gittip_username', list_display = ("user", "is_pro", "stripe_id", "gittip_username", "teams_beta_seen")
'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',)
admin.site.register(UserProfile, UserProfileAdmin) admin.site.register(UserProfile, UserProfileAdmin)

View File

@ -14,9 +14,9 @@ class Command(BaseCommand):
self.stdout.write(u"Updating %s user passwords..." % users.count()) self.stdout.write(u"Updating %s user passwords..." % users.count())
for user in users: for user in users:
if user.password[0:3] == 'bc$': if user.password[0:3] == "bc$":
pw = user.password pw = user.password
new_password = pw[0:3].replace('bc$', 'bcrypt$') + pw[3:] new_password = pw[0:3].replace("bc$", "bcrypt$") + pw[3:]
user.password = new_password user.password = new_password
user.save() user.save()

View File

@ -7,36 +7,115 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='UserProfile', name="UserProfile",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), (
('is_pro', models.BooleanField(default=False)), "id",
('teams_beta_seen', models.BooleanField(default=False)), models.AutoField(
('teams_beta_applied', models.BooleanField(default=False)), verbose_name="ID",
('pro_date', models.DateTimeField(null=True, blank=True)), serialize=False,
('stripe_id', models.CharField(max_length=100, null=True, blank=True)), auto_created=True,
('has_gravatar', models.BooleanField(default=False)), primary_key=True,
('list_view', models.CharField(default=b'N', max_length=1, choices=[(b'N', b'Normal'), (b'C', b'Compact')])), ),
('blog_title', models.CharField(max_length=250, null=True, blank=True)), ),
('blog_theme', models.CharField(default=b'A', max_length=1, choices=[(b'D', b'Default'), (b'A', b'Pro Adams')])), ("is_pro", models.BooleanField(default=False)),
('blog_domain', models.CharField(max_length=250, null=True, blank=True)), ("teams_beta_seen", models.BooleanField(default=False)),
('default_editor', models.CharField(default=b'C', max_length=250, choices=[(b'C', b'CodeMirror'), (b'T', b'Textarea')])), ("teams_beta_applied", models.BooleanField(default=False)),
('editor_theme', models.CharField(default=b'default', max_length=250, choices=[(b'default', b'Default'), (b'ambiance', b'Ambiance'), (b'blackboard', b'Blackboard'), (b'cobalt', b'Cobalt'), (b'eclipse', b'Eclipse'), (b'elegant', b'Elegant'), (b'erlang-dark', b'Erlang Dark'), (b'lesser-dark', b'Lesser Dark'), (b'monokai', b'Monokai'), (b'neat', b'Neat'), (b'night', b'Night'), (b'rubyblue', b'Ruby Blue'), (b'solarized dark', b'Solarized Dark'), (b'solarized light', b'Solarized Light'), (b'twilight', b'Twilight'), (b'vibrant-ink', b'Vibrant Ink'), (b'xq-dark', b'XQ Dark')])), ("pro_date", models.DateTimeField(null=True, blank=True)),
('gittip_username', models.CharField(max_length=250, null=True, blank=True)), ("stripe_id", models.CharField(max_length=100, null=True, blank=True)),
('disqus_shortname', models.CharField(max_length=250, null=True, blank=True)), ("has_gravatar", models.BooleanField(default=False)),
('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)), "list_view",
('google_ad_client', models.CharField(max_length=250, null=True, blank=True)), models.CharField(
('google_ad_slot', models.CharField(max_length=250, null=True, blank=True)), default=b"N",
('google_ad_width', models.CharField(max_length=250, null=True, blank=True)), max_length=1,
('google_ad_height', models.CharField(max_length=250, null=True, blank=True)), choices=[(b"N", b"Normal"), (b"C", b"Compact")],
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, unique=True)), ),
),
("blog_title", models.CharField(max_length=250, null=True, blank=True)),
(
"blog_theme",
models.CharField(
default=b"A",
max_length=1,
choices=[(b"D", b"Default"), (b"A", b"Pro Adams")],
),
),
(
"blog_domain",
models.CharField(max_length=250, null=True, blank=True),
),
(
"default_editor",
models.CharField(
default=b"C",
max_length=250,
choices=[(b"C", b"CodeMirror"), (b"T", b"Textarea")],
),
),
(
"editor_theme",
models.CharField(
default=b"default",
max_length=250,
choices=[
(b"default", b"Default"),
(b"ambiance", b"Ambiance"),
(b"blackboard", b"Blackboard"),
(b"cobalt", b"Cobalt"),
(b"eclipse", b"Eclipse"),
(b"elegant", b"Elegant"),
(b"erlang-dark", b"Erlang Dark"),
(b"lesser-dark", b"Lesser Dark"),
(b"monokai", b"Monokai"),
(b"neat", b"Neat"),
(b"night", b"Night"),
(b"rubyblue", b"Ruby Blue"),
(b"solarized dark", b"Solarized Dark"),
(b"solarized light", b"Solarized Light"),
(b"twilight", b"Twilight"),
(b"vibrant-ink", b"Vibrant Ink"),
(b"xq-dark", b"XQ Dark"),
],
),
),
(
"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_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_height",
models.CharField(max_length=250, null=True, blank=True),
),
("user", models.ForeignKey(to=settings.AUTH_USER_MODEL, unique=True)),
], ],
), )
] ]

View File

@ -7,14 +7,12 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0001_initial")]
('accounts', '0001_initial'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='userprofile', model_name="userprofile",
name='user', name="user",
field=models.OneToOneField(to=settings.AUTH_USER_MODEL), field=models.OneToOneField(to=settings.AUTH_USER_MODEL),
), )
] ]

View File

@ -6,29 +6,59 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0002_auto_20150724_2010")]
('accounts', '0002_auto_20150724_2010'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='userprofile', model_name="userprofile",
name='blog_theme', name="blog_theme",
field=models.CharField(max_length=1, choices=[('D', 'Default'), ('A', 'Pro Adams')], default='A'), field=models.CharField(
max_length=1,
choices=[("D", "Default"), ("A", "Pro Adams")],
default="A",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='userprofile', model_name="userprofile",
name='default_editor', name="default_editor",
field=models.CharField(max_length=250, choices=[('C', 'CodeMirror'), ('T', 'Textarea')], default='C'), field=models.CharField(
max_length=250,
choices=[("C", "CodeMirror"), ("T", "Textarea")],
default="C",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='userprofile', model_name="userprofile",
name='editor_theme', name="editor_theme",
field=models.CharField(max_length=250, choices=[('default', 'Default'), ('ambiance', 'Ambiance'), ('blackboard', 'Blackboard'), ('cobalt', 'Cobalt'), ('eclipse', 'Eclipse'), ('elegant', 'Elegant'), ('erlang-dark', 'Erlang Dark'), ('lesser-dark', 'Lesser Dark'), ('monokai', 'Monokai'), ('neat', 'Neat'), ('night', 'Night'), ('rubyblue', 'Ruby Blue'), ('solarized dark', 'Solarized Dark'), ('solarized light', 'Solarized Light'), ('twilight', 'Twilight'), ('vibrant-ink', 'Vibrant Ink'), ('xq-dark', 'XQ Dark')], default='default'), field=models.CharField(
max_length=250,
choices=[
("default", "Default"),
("ambiance", "Ambiance"),
("blackboard", "Blackboard"),
("cobalt", "Cobalt"),
("eclipse", "Eclipse"),
("elegant", "Elegant"),
("erlang-dark", "Erlang Dark"),
("lesser-dark", "Lesser Dark"),
("monokai", "Monokai"),
("neat", "Neat"),
("night", "Night"),
("rubyblue", "Ruby Blue"),
("solarized dark", "Solarized Dark"),
("solarized light", "Solarized Light"),
("twilight", "Twilight"),
("vibrant-ink", "Vibrant Ink"),
("xq-dark", "XQ Dark"),
],
default="default",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='userprofile', model_name="userprofile",
name='list_view', name="list_view",
field=models.CharField(max_length=1, choices=[('N', 'Normal'), ('C', 'Compact')], default='N'), field=models.CharField(
max_length=1, choices=[("N", "Normal"), ("C", "Compact")], default="N"
),
), ),
] ]

View File

@ -9,39 +9,30 @@ from teams.models import Team
class UserProfile(models.Model): class UserProfile(models.Model):
LIST_VIEW_CHOICES = ( LIST_VIEW_CHOICES = (("N", "Normal"), ("C", "Compact"))
('N', 'Normal'),
('C', 'Compact'),
)
EDITOR_CHOICES = ( EDITOR_CHOICES = (("C", "CodeMirror"), ("T", "Textarea"))
('C', 'CodeMirror'),
('T', 'Textarea'),
)
THEME_CHOICES = ( THEME_CHOICES = (("D", "Default"), ("A", "Pro Adams"))
('D', 'Default'),
('A', 'Pro Adams'),
)
EDITOR_THEME_CHOICES = ( EDITOR_THEME_CHOICES = (
('default', 'Default'), ("default", "Default"),
('ambiance', 'Ambiance'), ("ambiance", "Ambiance"),
('blackboard', 'Blackboard'), ("blackboard", "Blackboard"),
('cobalt', 'Cobalt'), ("cobalt", "Cobalt"),
('eclipse', 'Eclipse'), ("eclipse", "Eclipse"),
('elegant', 'Elegant'), ("elegant", "Elegant"),
('erlang-dark', 'Erlang Dark'), ("erlang-dark", "Erlang Dark"),
('lesser-dark', 'Lesser Dark'), ("lesser-dark", "Lesser Dark"),
('monokai', 'Monokai'), ("monokai", "Monokai"),
('neat', 'Neat'), ("neat", "Neat"),
('night', 'Night'), ("night", "Night"),
('rubyblue', 'Ruby Blue'), ("rubyblue", "Ruby Blue"),
('solarized dark', 'Solarized Dark'), ("solarized dark", "Solarized Dark"),
('solarized light', 'Solarized Light'), ("solarized light", "Solarized Light"),
('twilight', 'Twilight'), ("twilight", "Twilight"),
('vibrant-ink', 'Vibrant Ink'), ("vibrant-ink", "Vibrant Ink"),
('xq-dark', 'XQ Dark'), ("xq-dark", "XQ Dark"),
) )
# User # User
@ -52,27 +43,35 @@ class UserProfile(models.Model):
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, list_view = models.CharField(
default='N', choices=LIST_VIEW_CHOICES) 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, blog_theme = models.CharField(
default='A', choices=THEME_CHOICES) max_length=1, null=False, blank=False, default="A", choices=THEME_CHOICES
)
blog_domain = models.CharField(max_length=250, null=True, blank=True) 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_editor = models.CharField(
default='C', choices=EDITOR_CHOICES) 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', editor_theme = models.CharField(
choices=EDITOR_THEME_CHOICES) 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, google_analytics_tracking_id = models.CharField(
blank=True) max_length=250, null=True, blank=True
)
gauges_site_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 Ads
@ -82,36 +81,34 @@ class UserProfile(models.Model):
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, return Snipt.objects.filter(user=self.user, blog_post=True, public=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:
return None return None
else: else:
return self.blog_domain.split(' ')[0] return self.blog_domain.split(" ")[0]
def get_user_profile_url(self): def get_user_profile_url(self):
# If the user has a blog domain, use that. # If the user has a blog domain, use that.
if self.blog_domain: if self.blog_domain:
url = 'http://{}'.format(self.get_primary_blog_domain()) url = "http://{}".format(self.get_primary_blog_domain())
# Otherwise, if they have blog posts, use their Snipt blog URL. # Otherwise, if they have blog posts, use their Snipt blog URL.
elif self.get_blog_posts(): elif self.get_blog_posts():
url = 'https://{}.snippets.siftie.com/'.format(self.user.username) url = "https://{}.snippets.siftie.com/".format(self.user.username)
# Otherwise, use their regular Snipt profile page. # Otherwise, use their regular Snipt profile page.
else: else:
url = 'https://snippets.siftie.com/{}/'.format(self.user.username) url = "https://snippets.siftie.com/{}/".format(self.user.username)
return url return url
def has_public_snipts(self): def has_public_snipts(self):
return True \ return (
if Snipt.objects.filter(user=self, True if Snipt.objects.filter(user=self, public=True).count() > 0 else False
public=True).count() > 0 \ )
else False
@property @property
def is_a_team(self): def is_a_team(self):
@ -127,14 +124,16 @@ class UserProfile(models.Model):
@property @property
def has_teams(self): def has_teams(self):
if (len(self.teams()) > 0): if len(self.teams()) > 0:
return True return True
else: else:
return False return False
def get_account_age(self): def get_account_age(self):
delta = datetime.now().replace(tzinfo=None) - \ delta = datetime.now().replace(tzinfo=None) - self.user.date_joined.replace(
self.user.date_joined.replace(tzinfo=None) 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

@ -3,6 +3,6 @@ from django.conf.urls import url
urlpatterns = [ urlpatterns = [
url(r'^stats/$', views.stats, name='account-stats'), url(r"^stats/$", views.stats, name="account-stats"),
url(r'^', views.account, name='account-detail') url(r"^", views.account, name="account-detail"),
] ]

View File

@ -5,17 +5,15 @@ from snipts.models import Snipt
@login_required @login_required
@render_to('account.html') @render_to("account.html")
def account(request): def account(request):
return {} return {}
@login_required @login_required
@render_to('stats.html') @render_to("stats.html")
def stats(request): def stats(request):
snipts = Snipt.objects.filter(user=request.user).order_by('-views') snipts = Snipt.objects.filter(user=request.user).order_by("-views")
return { return {"snipts": snipts}
'snipts': snipts
}

View File

@ -8,43 +8,43 @@ class BlogMiddleware:
def process_request(self, request): def process_request(self, request):
request.blog_user = None request.blog_user = None
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 != 'snippets.siftie.com' and \ if (
host != 'snipt.localhost' and \ host != "snippets.siftie.com"
host != 'local.snippets.siftie.com': and host != "snipt.localhost"
and host != "local.snippets.siftie.com"
):
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 = \ request.blog_user = get_object_or_None(
get_object_or_None(User, User, username__iexact=blog_user
username__iexact=blog_user) )
if request.blog_user is None: if request.blog_user is None:
request.blog_user = \ request.blog_user = get_object_or_404(
get_object_or_404(User, User, username__iexact=blog_user.replace("-", "_")
username__iexact=blog_user )
.replace('-', '_'))
else: else:
request.blog_user = \ request.blog_user = get_object_or_404(
get_object_or_404(User, username__iexact=blog_user) 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)
for pro_user in pro_users: for pro_user in pro_users:
if pro_user.profile.blog_domain: if pro_user.profile.blog_domain:
if host in pro_user.profile.blog_domain.split(' '): if host in pro_user.profile.blog_domain.split(" "):
request.blog_user = pro_user request.blog_user = pro_user
if host != \ if host != pro_user.profile.get_primary_blog_domain():
pro_user.profile.get_primary_blog_domain():
return HttpResponseRedirect( return HttpResponseRedirect(
'http://' + "http://"
pro_user + pro_user.profile.get_primary_blog_domain()
.profile )
.get_primary_blog_domain())

View File

@ -2,4 +2,4 @@ from blogs import views
from django.conf.urls import url from django.conf.urls import url
urlpatterns = [url(r'^$', views.blog, name='blog')] urlpatterns = [url(r"^$", views.blog, name="blog")]

View File

@ -4,10 +4,7 @@ from annoying.functions import get_object_or_None
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from snipts.models import Snipt from snipts.models import Snipt
THEME_CHOICES = { THEME_CHOICES = {"D": "blogs/themes/default/", "A": "blogs/themes/pro-adams/"}
'D': 'blogs/themes/default/',
'A': 'blogs/themes/pro-adams/',
}
def blog_list(request, username_or_custom_slug=None): def blog_list(request, username_or_custom_slug=None):
@ -15,83 +12,95 @@ def blog_list(request, username_or_custom_slug=None):
if username_or_custom_slug: if username_or_custom_slug:
return blog_post(request, username_or_custom_slug) return blog_post(request, username_or_custom_slug)
snipts = Snipt.objects.filter(user=request.blog_user, snipts = (
blog_post=True, Snipt.objects.filter(
public=True, user=request.blog_user,
publish_date__lte=datetime.datetime.now()) \ blog_post=True,
.order_by('-publish_date') \ public=True,
.exclude(title__iexact='Homepage') \ publish_date__lte=datetime.datetime.now(),
.exclude(title__iexact='Work') )
.order_by("-publish_date")
.exclude(title__iexact="Homepage")
.exclude(title__iexact="Work")
)
normal_snipts = Snipt.objects.filter(blog_post=False, normal_snipts = Snipt.objects.filter(
user=request.blog_user, blog_post=False, user=request.blog_user, public=True
public=True) \ ).order_by("-created")
.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, sidebar = get_object_or_None(
title='Sidebar', blog_post=True) 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) header = get_object_or_None(
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', Snipt, user=request.blog_user, title="Header", blog_post=True
lexer='css', 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,
'custom_css': custom_css, "custom_css": custom_css,
'has_snipts': True, "has_snipts": True,
'header': header, "header": header,
'normal_snipts': normal_snipts, "normal_snipts": normal_snipts,
'public': True, "public": True,
'sidebar': sidebar, "sidebar": sidebar,
'snipts': snipts, "snipts": snipts,
} }
if 'rss' in request.GET: if "rss" in request.GET:
context['snipts'] = context['snipts'][:20] context["snipts"] = context["snipts"][:20]
return rss(request, context) return rss(request, context)
template = THEME_CHOICES[request.blog_user.profile.blog_theme] template = THEME_CHOICES[request.blog_user.profile.blog_theme]
template = '{}/list.html'.format(template) template = "{}/list.html".format(template)
return render( return render(request, template, context)
request,
template,
context,
)
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(
blog_post=True, Snipt,
public=True, user=request.blog_user,
publish_date__lte=datetime.datetime.now(), blog_post=True,
slug=username_or_custom_slug) public=True,
publish_date__lte=datetime.datetime.now(),
slug=username_or_custom_slug,
)
snipts = Snipt.objects.filter(user=request.blog_user, snipts = (
blog_post=True, Snipt.objects.filter(
public=True, user=request.blog_user,
publish_date__lte=datetime.datetime.now()) \ blog_post=True,
.order_by('-publish_date') \ public=True,
.exclude(title__iexact='Homepage') \ publish_date__lte=datetime.datetime.now(),
.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, sidebar = get_object_or_None(
title='Sidebar', blog_post=True) 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) header = get_object_or_None(
custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', Snipt, user=request.blog_user, title="Header", blog_post=True
lexer='css', 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, normal_snipts = Snipt.objects.filter(
user=request.blog_user, blog_post=False, user=request.blog_user, public=True
public=True).order_by('-created') ).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]
if snipt.user != request.user: if snipt.user != request.user:
@ -99,25 +108,29 @@ def blog_post(request, username_or_custom_slug):
snipt.save() snipt.save()
context = { context = {
'blog_user': request.blog_user, "blog_user": request.blog_user,
'custom_css': custom_css, "custom_css": custom_css,
'detail': True, "detail": True,
'has_snipts': True, "has_snipts": True,
'header': header, "header": header,
'normal_snipts': normal_snipts, "normal_snipts": normal_snipts,
'public': True, "public": True,
'sidebar': sidebar, "sidebar": sidebar,
'snipt': snipt, "snipt": snipt,
'snipts': snipts, "snipts": snipts,
} }
template = THEME_CHOICES[request.blog_user.profile.blog_theme] template = THEME_CHOICES[request.blog_user.profile.blog_theme]
template = '{}/post.html'.format(template) template = "{}/post.html".format(template)
return render(request, template, context) return render(request, template, context)
def rss(request, context): def rss(request, context):
return render(request, 'blogs/themes/default/rss.xml', context, return render(
content_type="application/rss+xml") request,
"blogs/themes/default/rss.xml",
context,
content_type="application/rss+xml",
)

View File

@ -3,134 +3,132 @@ from urllib.parse import urlparse
import dj_database_url import dj_database_url
import os import os
if 'DATABASE_URL' in os.environ: if "DATABASE_URL" in os.environ:
DATABASES = {'default': dj_database_url.config()} DATABASES = {"default": dj_database_url.config()}
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 = (('Siftie', 'team@siftie.com'),) ADMINS = (("Siftie", "team@siftie.com"),)
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ["*"]
AUTH_PROFILE_MODULE = 'accounts.UserProfile' AUTH_PROFILE_MODULE = "accounts.UserProfile"
AUTHENTICATION_BACKENDS = ('utils.backends.EmailOrUsernameModelBackend',) AUTHENTICATION_BACKENDS = ("utils.backends.EmailOrUsernameModelBackend",)
BASE_PATH = os.path.dirname(__file__) BASE_PATH = os.path.dirname(__file__)
CSRF_COOKIE_SECURE = True if 'USE_SSL' in os.environ else False CSRF_COOKIE_SECURE = True if "USE_SSL" in os.environ else False
CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_ALLOW_ALL = True
DEBUG = True if 'DEBUG' in os.environ else False DEBUG = True if "DEBUG" in os.environ else False
DEFAULT_FROM_EMAIL = os.environ.get('POSTMARK_EMAIL', 'support@siftie.com') DEFAULT_FROM_EMAIL = os.environ.get("POSTMARK_EMAIL", "support@siftie.com")
EMAIL_BACKEND = 'postmark.django_backend.EmailBackend' EMAIL_BACKEND = "postmark.django_backend.EmailBackend"
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
INTERNAL_IPS = ('127.0.0.1',) INTERNAL_IPS = ("127.0.0.1",)
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = "en-us"
LOGIN_REDIRECT_URL = '/login-redirect/' LOGIN_REDIRECT_URL = "/login-redirect/"
LOGIN_URL = '/login/' LOGIN_URL = "/login/"
LOGOUT_URL = '/logout/' LOGOUT_URL = "/logout/"
MANAGERS = ADMINS MANAGERS = ADMINS
MEDIA_ROOT = os.path.join(BASE_PATH, 'media/uploads') MEDIA_ROOT = os.path.join(BASE_PATH, "media/uploads")
MEDIA_URL = '/media/uploads/' MEDIA_URL = "/media/uploads/"
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' MESSAGE_STORAGE = "django.contrib.messages.storage.cookie.CookieStorage"
PASSWORD_HASHERS = ( PASSWORD_HASHERS = (
'django.contrib.auth.hashers.BCryptPasswordHasher', "django.contrib.auth.hashers.BCryptPasswordHasher",
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', "django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
'django.contrib.auth.hashers.PBKDF2PasswordHasher', "django.contrib.auth.hashers.PBKDF2PasswordHasher",
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
'django.contrib.auth.hashers.SHA1PasswordHasher', "django.contrib.auth.hashers.SHA1PasswordHasher",
'django.contrib.auth.hashers.MD5PasswordHasher', "django.contrib.auth.hashers.MD5PasswordHasher",
'django.contrib.auth.hashers.CryptPasswordHasher', "django.contrib.auth.hashers.CryptPasswordHasher",
) )
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__))
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")
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True if 'USE_SSL' in os.environ else False SECURE_SSL_REDIRECT = True if "USE_SSL" in os.environ else False
SEND_BROKEN_LINK_EMAILS = False SEND_BROKEN_LINK_EMAILS = False
SERVER_EMAIL = os.environ.get('POSTMARK_EMAIL', 'support@siftie.com') SERVER_EMAIL = os.environ.get("POSTMARK_EMAIL", "support@siftie.com")
SESSION_COOKIE_AGE = 15801100 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', STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.AppDirectoriesFinder') "django.contrib.staticfiles.finders.FileSystemFinder",
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' "django.contrib.staticfiles.finders.AppDirectoriesFinder",
STATIC_ROOT = os.path.join(BASE_PATH, 'static') )
STATIC_URL = '/static/' STATICFILES_STORAGE = "whitenoise.django.GzipManifestStaticFilesStorage"
STATIC_ROOT = os.path.join(BASE_PATH, "static")
STATIC_URL = "/static/"
TASTYPIE_CANNED_ERROR = """There was an error with your request. The site TASTYPIE_CANNED_ERROR = """There was an error with your request. The site
developers have a record of this error, please email support@siftie.com and developers have a record of this error, please email support@siftie.com and
we'll help you out.""" we'll help you out."""
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [os.path.join(PROJECT_PATH, 'templates')], "DIRS": [os.path.join(PROJECT_PATH, "templates")],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.template.context_processors.static', "django.template.context_processors.static",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ]
}, },
}, }
] ]
TIME_ZONE = 'America/New_York' TIME_ZONE = "America/New_York"
USE_HTTPS = True if 'USE_SSL' in os.environ else False USE_HTTPS = True if "USE_SSL" in os.environ else False
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True
USE_TZ = True USE_TZ = True
INSTALLED_APPS = ( INSTALLED_APPS = (
'accounts', "accounts",
'blogs', "blogs",
'corsheaders', "corsheaders",
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.humanize', "django.contrib.humanize",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.sites', "django.contrib.sites",
'django.contrib.staticfiles', "django.contrib.staticfiles",
'django_extensions', "django_extensions",
'gunicorn', "gunicorn",
'haystack', "haystack",
'markdown_deux', "markdown_deux",
'pagination', "pagination",
'postmark', "postmark",
'registration', "registration",
'snipts', "snipts",
'storages', "storages",
'taggit', "taggit",
'tastypie', "tastypie",
'teams', "teams",
'user-admin', "user-admin",
'utils', "utils",
) )
LOGGING = { LOGGING = {
'version': 1, "version": 1,
'disable_existing_loggers': False, "disable_existing_loggers": False,
'filters': { "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
'require_debug_false': { "handlers": {},
'()': 'django.utils.log.RequireDebugFalse' "loggers": {},
}
},
'handlers': {},
'loggers': {}
} }
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
'corsheaders.middleware.CorsMiddleware', "corsheaders.middleware.CorsMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'pagination.middleware.PaginationMiddleware', "pagination.middleware.PaginationMiddleware",
'blogs.middleware.BlogMiddleware', "blogs.middleware.BlogMiddleware",
) )
try: try:

View File

@ -3,29 +3,42 @@ from snipts.models import Favorite, Snipt, SniptLogEntry
class SniptAdmin(admin.ModelAdmin): class SniptAdmin(admin.ModelAdmin):
readonly_fields = ('last_user_saved', 'user',) readonly_fields = ("last_user_saved", "user")
list_display = ('title', 'slug', 'views', 'favs', 'user', 'lexer', list_display = (
'public', 'blog_post', 'created', 'modified', "title",
'publish_date') "slug",
list_filter = ('blog_post',) "views",
search_fields = ('title', 'slug', 'user__username', 'lexer', 'id', 'key',) "favs",
ordering = ('-created',) "user",
prepopulated_fields = {'slug': ('title',)} "lexer",
"public",
"blog_post",
"created",
"modified",
"publish_date",
)
list_filter = ("blog_post",)
search_fields = ("title", "slug", "user__username", "lexer", "id", "key")
ordering = ("-created",)
prepopulated_fields = {"slug": ("title",)}
admin.site.register(Snipt, SniptAdmin) admin.site.register(Snipt, SniptAdmin)
class SniptLogEntryAdmin(admin.ModelAdmin): class SniptLogEntryAdmin(admin.ModelAdmin):
readonly_fields = ('snipt', 'user',) readonly_fields = ("snipt", "user")
list_display = ('snipt_name', 'user', 'created', 'modified') list_display = ("snipt_name", "user", "created", "modified")
admin.site.register(SniptLogEntry, SniptLogEntryAdmin) admin.site.register(SniptLogEntry, SniptLogEntryAdmin)
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")
search_fields = ('snipt', 'user',) search_fields = ("snipt", "user")
ordering = ('-created',) ordering = ("-created",)
admin.site.register(Favorite, FavoriteAdmin) admin.site.register(Favorite, FavoriteAdmin)

View File

@ -139,11 +139,10 @@ class PrivateUserAuthorization(Authorization):
class FavoriteValidation(Validation): class FavoriteValidation(Validation):
def is_valid(self, bundle, request=None): def is_valid(self, bundle, request=None):
errors = {} errors = {}
snipt = bundle.data['snipt'] snipt = bundle.data["snipt"]
if Favorite.objects.filter(user=bundle.request.user, if Favorite.objects.filter(user=bundle.request.user, snipt=snipt).count():
snipt=snipt).count(): errors = "User has already favorited this snipt."
errors = 'User has already favorited this snipt.'
return errors return errors
@ -152,8 +151,8 @@ class SniptValidation(Validation):
def is_valid(self, bundle, request=None): def is_valid(self, bundle, request=None):
errors = {} errors = {}
if (len(bundle.data['title']) > 255): if len(bundle.data["title"]) > 255:
errors = 'Title must be 255 characters or less.' errors = "Title must be 255 characters or less."
return errors return errors
@ -164,10 +163,12 @@ 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, ' errors[field] = (
'underscores, dashes, periods, forward ' "Only spaces, letters, numbers, "
'slashes, and "at sign" are valid.') "underscores, dashes, periods, forward "
'slashes, and "at sign" are valid.'
)
return errors return errors
@ -175,33 +176,33 @@ class UserProfileValidation(Validation):
class PublicUserResource(ModelResource): 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': ['contains', 'exact']} filtering = {"username": ["contains", "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 \ bundle.data["email_md5"] = hashlib.md5(
.md5(bundle.obj.email.lower().encode('utf-8')) \ bundle.obj.email.lower().encode("utf-8")
.hexdigest() ).hexdigest()
bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id, bundle.data["snipts_count"] = Snipt.objects.filter(
public=True).count() 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( queryset = queryset.annotate(count=models.Count("taggit_taggeditem_items__id"))
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()
@ -211,60 +212,74 @@ class PublicTagResource(ModelResource):
orm_filters = super(PublicTagResource, self).build_filters(filters) orm_filters = super(PublicTagResource, self).build_filters(filters)
if 'q' in filters: if "q" in filters:
orm_filters['slug'] = filters['q'] orm_filters["slug"] = filters["q"]
return orm_filters return orm_filters
def dehydrate(self, bundle): def dehydrate(self, bundle):
bundle.data['absolute_url'] = '/public/tag/%s/' % bundle.obj.slug bundle.data["absolute_url"] = "/public/tag/%s/" % bundle.obj.slug
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', tags = fields.ToManyField(PublicTagResource, "tags", related_name="tag", full=True)
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', fields = [
'line_count', 'stylized', 'created', 'modified', "id",
'publish_date', 'blog_post', 'meta'] "title",
"slug",
"lexer",
"code",
"description",
"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()
def dehydrate(self, bundle): def dehydrate(self, bundle):
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'] = \ bundle.data["description_rendered"] = linebreaksbr(
linebreaksbr(urlize(bundle.obj.description)) urlize(bundle.obj.description)
)
log_entries = bundle.obj.sniptlogentry_set.all() log_entries = bundle.obj.sniptlogentry_set.all()
bundle_log_entries = [] bundle_log_entries = []
for entry in log_entries: for entry in log_entries:
bundle_log_entries.append({ bundle_log_entries.append(
'created': entry.created, {
'user': entry.user, "created": entry.created,
'code': entry.code, "user": entry.user,
'diff': entry.diff "code": entry.code,
}) "diff": entry.diff,
}
)
bundle.data['log_entries'] = bundle_log_entries bundle.data["log_entries"] = bundle_log_entries
if 'omit_code' in bundle.request.GET: if "omit_code" in bundle.request.GET:
del bundle.data['code'] del bundle.data["code"]
if 'omit_stylized' in bundle.request.GET: if "omit_stylized" in bundle.request.GET:
del bundle.data['stylized'] del bundle.data["stylized"]
return bundle return bundle
@ -274,14 +289,14 @@ class PublicSniptResource(ModelResource):
orm_filters = super(PublicSniptResource, self).build_filters(filters) orm_filters = super(PublicSniptResource, self).build_filters(filters)
if 'tag' in filters: if "tag" in filters:
tag = Tag.objects.get(pk=filters['tag']) tag = Tag.objects.get(pk=filters["tag"])
tagged_items = tag.taggit_taggeditem_items.all() tagged_items = tag.taggit_taggeditem_items.all()
orm_filters['pk__in'] = [i.object_id for i in tagged_items] orm_filters["pk__in"] = [i.object_id for i in tagged_items]
if 'q' in filters: if "q" in filters:
sqs = SearchQuerySet().auto_query(filters['q']) sqs = SearchQuerySet().auto_query(filters["q"])
orm_filters['pk__in'] = [i.pk for i in sqs] orm_filters["pk__in"] = [i.pk for i in sqs]
return orm_filters return orm_filters
@ -289,11 +304,11 @@ class PublicSniptResource(ModelResource):
class PrivateUserProfileResource(ModelResource): class PrivateUserProfileResource(ModelResource):
class Meta: class Meta:
queryset = UserProfile.objects.all() queryset = UserProfile.objects.all()
resource_name = 'profile' resource_name = "profile"
excludes = ['is_pro'] excludes = ["is_pro"]
validation = UserProfileValidation() validation = UserProfileValidation()
include_absolute_url = False include_absolute_url = False
allowed_methods = ['get', 'put'] allowed_methods = ["get", "put"]
list_allowed_methods = [] list_allowed_methods = []
authentication = ApiKeyAuthentication() authentication = ApiKeyAuthentication()
authorization = PrivateUserProfileAuthorization() authorization = PrivateUserProfileAuthorization()
@ -301,23 +316,22 @@ class PrivateUserProfileResource(ModelResource):
max_limit = 200 max_limit = 200
def dehydrate(self, bundle): def dehydrate(self, bundle):
bundle.data['email'] = bundle.obj.user.email bundle.data["email"] = bundle.obj.user.email
bundle.data['username'] = bundle.obj.user.username bundle.data["username"] = bundle.obj.user.username
bundle.data['user_id'] = bundle.obj.user.id bundle.data["user_id"] = bundle.obj.user.id
bundle.data['api_key'] = bundle.obj.user.api_key.key bundle.data["api_key"] = bundle.obj.user.api_key.key
return bundle return bundle
class PrivateUserResource(ModelResource): class PrivateUserResource(ModelResource):
profile = fields.ForeignKey(PrivateUserProfileResource, 'profile', profile = fields.ForeignKey(PrivateUserProfileResource, "profile", full=False)
full=False)
class Meta: class Meta:
queryset = User.objects.all() queryset = User.objects.all()
resource_name = 'user' resource_name = "user"
fields = ['id', 'username', 'email'] fields = ["id", "username", "email"]
include_absolute_url = True include_absolute_url = True
allowed_methods = ['get'] allowed_methods = ["get"]
list_allowed_methods = [] list_allowed_methods = []
authentication = ApiKeyAuthentication() authentication = ApiKeyAuthentication()
authorization = PrivateUserAuthorization() authorization = PrivateUserAuthorization()
@ -326,155 +340,172 @@ class PrivateUserResource(ModelResource):
cache = SimpleCache() cache = SimpleCache()
def dehydrate(self, bundle): def dehydrate(self, bundle):
bundle.data['email_md5'] = hashlib \ bundle.data["email_md5"] = hashlib.md5(
.md5(bundle.obj.email.lower().encode('utf-8')) \ bundle.obj.email.lower().encode("utf-8")
.hexdigest() ).hexdigest()
bundle.data['stats'] = { bundle.data["stats"] = {
'public_snipts': Snipt.objects.filter(user=bundle.obj.id, "public_snipts": Snipt.objects.filter(
public=True).count(), user=bundle.obj.id, public=True
'private_snipts': Snipt.objects.filter(user=bundle.obj.id, ).count(),
public=False).count(), "private_snipts": Snipt.objects.filter(
'total_snipts': Snipt.objects.filter(user=bundle.obj.id).count(), user=bundle.obj.id, public=False
'total_views': Snipt.objects.filter(user=bundle.obj.id).aggregate( ).count(),
models.Sum('views'))['views__sum'] "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"],
} }
user_snipts = Snipt.objects.filter(user=bundle.obj) user_snipts = Snipt.objects.filter(user=bundle.obj)
user_tags = [ user_tags = [snipt["tags"] for snipt in user_snipts.values("tags").distinct()]
snipt['tags'] for snipt in user_snipts.values('tags').distinct()
]
tags = [ tags = [
tag['name'] for tag in tag["name"]
Tag.objects.filter(id__in=user_tags).values('name').distinct() for tag in Tag.objects.filter(id__in=user_tags).values("name").distinct()
] ]
bundle.data['tags'] = tags bundle.data["tags"] = tags
bundle.data['lexers'] = [ bundle.data["lexers"] = [
snipt['lexer'] for snipt in user_snipts snipt["lexer"] for snipt in user_snipts.values("lexer").distinct()
.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)
last_user_saved = fields.ForeignKey(PrivateUserResource, last_user_saved = fields.ForeignKey(
'last_user_saved', PrivateUserResource, "last_user_saved", null=True, full=False
null=True, )
full=False)
tags_list = ListField() tags_list = ListField()
tags = fields.ToManyField(PublicTagResource, 'tags', related_name='tag', tags = fields.ToManyField(PublicTagResource, "tags", related_name="tag", full=True)
full=True)
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', fields = [
'line_count', 'stylized', 'key', 'public', 'secure', "id",
'blog_post', 'created', 'modified', 'publish_date', 'meta'] "title",
"slug",
"lexer",
"code",
"description",
"line_count",
"stylized",
"key",
"public",
"secure",
"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()
def dehydrate(self, bundle): def dehydrate(self, bundle):
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['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'] = \ bundle.data["description_rendered"] = linebreaksbr(
linebreaksbr(urlize(bundle.obj.description)) urlize(bundle.obj.description)
bundle.data['views'] = bundle.obj.views )
bundle.data['favs'] = bundle.obj.favs() bundle.data["views"] = bundle.obj.views
bundle.data["favs"] = bundle.obj.favs()
if bundle.data['publish_date']: if bundle.data["publish_date"]:
bundle.data['publish_date'] = \ bundle.data["publish_date"] = date(
date(bundle.data['publish_date'], 'M d, Y \\a\\t h:i A') bundle.data["publish_date"], "M d, Y \\a\\t h:i A"
)
log_entries = bundle.obj.sniptlogentry_set.all() log_entries = bundle.obj.sniptlogentry_set.all()
bundle_log_entries = [] bundle_log_entries = []
for entry in log_entries: for entry in log_entries:
bundle_log_entries.append({ bundle_log_entries.append(
'created': entry.created, {
'user': entry.user, "created": entry.created,
'code': entry.code, "user": entry.user,
'diff': entry.diff "code": entry.code,
}) "diff": entry.diff,
}
)
bundle.data['log_entries'] = bundle_log_entries bundle.data["log_entries"] = bundle_log_entries
return bundle return bundle
def obj_create(self, bundle, **kwargs): def obj_create(self, bundle, **kwargs):
bundle.data['last_user_saved'] = bundle.request.user bundle.data["last_user_saved"] = bundle.request.user
bundle.data['tags_list'] = bundle.data.get('tags') bundle.data["tags_list"] = bundle.data.get("tags")
bundle.data['tags'] = [] bundle.data["tags"] = []
if 'intended_user' in bundle.data: if "intended_user" in bundle.data:
bundle.data['user'] = \ bundle.data["user"] = User.objects.get(
User.objects.get(username=bundle.data['intended_user']) username=bundle.data["intended_user"]
)
else: else:
bundle.data['user'] = bundle.request.user bundle.data["user"] = bundle.request.user
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) \ return super(PrivateSniptResource, self).obj_create(bundle, **kwargs)
.obj_create(bundle, **kwargs)
def obj_update(self, bundle, **kwargs): def obj_update(self, bundle, **kwargs):
instance = Snipt.objects.get(pk=bundle.data['id']) instance = Snipt.objects.get(pk=bundle.data["id"])
if (instance.user.profile.is_a_team): if instance.user.profile.is_a_team:
user = instance.user user = instance.user
else: else:
user = bundle.request.user user = bundle.request.user
bundle.data['created'] = None bundle.data["created"] = None
bundle.data['last_user_saved'] = bundle.request.user bundle.data["last_user_saved"] = bundle.request.user
bundle.data['modified'] = None bundle.data["modified"] = None
bundle.data['user'] = user bundle.data["user"] = user
if type(bundle.data['tags']) == str or type(bundle.data['tags']) == unicode: if type(bundle.data["tags"]) == str or type(bundle.data["tags"]) == unicode:
bundle.data['tags_list'] = bundle.data['tags'] bundle.data["tags_list"] = bundle.data["tags"]
else: else:
bundle.data['tags_list'] = '' bundle.data["tags_list"] = ""
bundle.data['tags'] = '' bundle.data["tags"] = ""
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) \ return super(PrivateSniptResource, self).obj_update(bundle, **kwargs)
.obj_update(bundle, **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:
bundle.data['publish_date'] = datetime.datetime.now() bundle.data["publish_date"] = datetime.datetime.now()
elif bundle.data['publish_date'] == '': elif bundle.data["publish_date"] == "":
bundle.data['publish_date'] = datetime.datetime.now() bundle.data["publish_date"] = datetime.datetime.now()
elif bundle.data['blog_post']: elif bundle.data["blog_post"]:
c = pdt.Constants() c = pdt.Constants()
p = pdt.Calendar(c) p = pdt.Calendar(c)
publish_date, result = p.parse(bundle.data['publish_date']) publish_date, result = p.parse(bundle.data["publish_date"])
if result != 0: if result != 0:
publish_date = time.strftime('%Y-%m-%d %H:%M:%S', publish_date) publish_date = time.strftime("%Y-%m-%d %H:%M:%S", publish_date)
else: else:
publish_date = datetime.datetime.now() publish_date = datetime.datetime.now()
bundle.data['publish_date'] = publish_date bundle.data["publish_date"] = publish_date
elif not bundle.data['blog_post']: elif not bundle.data["blog_post"]:
bundle.data['publish_date'] = None bundle.data["publish_date"] = None
return bundle return bundle
@ -484,51 +515,51 @@ class PrivateSniptResource(ModelResource):
orm_filters = super(PrivateSniptResource, self).build_filters(filters) orm_filters = super(PrivateSniptResource, self).build_filters(filters)
if 'tag' in filters: if "tag" in filters:
tag = Tag.objects.get(pk=filters['tag']) tag = Tag.objects.get(pk=filters["tag"])
tagged_items = tag.taggit_taggeditem_items.all() tagged_items = tag.taggit_taggeditem_items.all()
orm_filters['pk__in'] = [i.object_id for i in tagged_items] orm_filters["pk__in"] = [i.object_id for i in tagged_items]
if 'q' in filters: if "q" in filters:
user = User.objects.get(username=filters['username']) user = User.objects.get(username=filters["username"])
sqs = SearchQuerySet().filter(author=user, content=filters['q']) sqs = SearchQuerySet().filter(author=user, content=filters["q"])
orm_filters['pk__in'] = [i.pk for i in sqs] orm_filters["pk__in"] = [i.pk for i in sqs]
return orm_filters return orm_filters
def save_m2m(self, bundle): def save_m2m(self, bundle):
tags = bundle.data.get('tags_list', []) tags = bundle.data.get("tags_list", [])
if tags != '': if tags != "":
bundle.obj.tags.set(*parse_tags(tags)) bundle.obj.tags.set(*parse_tags(tags))
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)
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) \ return super(PrivateFavoriteResource, self).obj_create(
.obj_create(bundle, user=bundle.request.user, **kwargs) bundle, user=bundle.request.user, **kwargs
)

View File

@ -8,15 +8,22 @@ import requests
def get_snipts(api_key, from_username, url=None, snipts=[]): def get_snipts(api_key, from_username, url=None, snipts=[]):
path = url or '/api/private/snipt/?limit=50&api_key={}&username={}&format=json'.format(api_key, from_username) path = (
res = requests.get('https://snippets.siftie.com' + path) url
or "/api/private/snipt/?limit=50&api_key={}&username={}&format=json".format(
api_key, from_username
)
)
res = requests.get("https://snippets.siftie.com" + path)
json = res.json() json = res.json()
print(u"Fetched snipts {} through {} of {}".format( print(
json["meta"]["offset"], u"Fetched snipts {} through {} of {}".format(
json["meta"]["offset"] + json["meta"]["limit"], json["meta"]["offset"],
json["meta"]["total_count"] json["meta"]["offset"] + json["meta"]["limit"],
)) json["meta"]["total_count"],
)
)
snipts.extend(json["objects"]) snipts.extend(json["objects"])
@ -30,14 +37,14 @@ class Command(BaseCommand):
help = u"Import snipts from Siftie Snippets." help = u"Import snipts from Siftie Snippets."
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('api_key', nargs='+', type=str) parser.add_argument("api_key", nargs="+", type=str)
parser.add_argument('from_username', nargs='+', type=str) parser.add_argument("from_username", nargs="+", type=str)
parser.add_argument('to_username', nargs='+', type=str) parser.add_argument("to_username", nargs="+", type=str)
def handle(self, *args, **options): def handle(self, *args, **options):
api_key = options['api_key'][0] api_key = options["api_key"][0]
from_username = options['from_username'][0] from_username = options["from_username"][0]
to_username = options['to_username'][0] to_username = options["to_username"][0]
to_user = User.objects.get(username=to_username) to_user = User.objects.get(username=to_username)
print(u"Fetching snipts...") print(u"Fetching snipts...")
@ -62,7 +69,7 @@ class Command(BaseCommand):
stylized=snipt["stylized"], stylized=snipt["stylized"],
title=snipt["title"], title=snipt["title"],
user=to_user, user=to_user,
views=snipt["views"] views=snipt["views"],
) )
s.created = snipt["created"] s.created = snipt["created"]

View File

@ -9,53 +9,90 @@ import taggit.managers
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('taggit', '0001_initial'), ("taggit", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Favorite', name="Favorite",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), (
('created', models.DateTimeField(auto_now_add=True)), "id",
('modified', models.DateTimeField(auto_now=True)), models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
("created", models.DateTimeField(auto_now_add=True)),
("modified", models.DateTimeField(auto_now=True)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Snipt', name="Snipt",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), (
('title', models.CharField(default=b'Untitled', max_length=255, null=True, blank=True)), "id",
('slug', models.SlugField(max_length=255, blank=True)), models.AutoField(
('custom_slug', models.SlugField(max_length=255, blank=True)), verbose_name="ID",
('lexer', models.CharField(max_length=50)), serialize=False,
('code', models.TextField()), auto_created=True,
('meta', models.TextField(null=True, blank=True)), primary_key=True,
('description', models.TextField(null=True, blank=True)), ),
('stylized', models.TextField(null=True, blank=True)), ),
('stylized_min', models.TextField(null=True, blank=True)), (
('embedded', models.TextField(null=True, blank=True)), "title",
('line_count', models.IntegerField(default=None, null=True, blank=True)), models.CharField(
('key', models.CharField(max_length=100, null=True, blank=True)), default=b"Untitled", max_length=255, null=True, blank=True
('public', models.BooleanField(default=False)), ),
('blog_post', models.BooleanField(default=False)), ),
('views', models.IntegerField(default=0)), ("slug", models.SlugField(max_length=255, blank=True)),
('created', models.DateTimeField(auto_now_add=True)), ("custom_slug", models.SlugField(max_length=255, blank=True)),
('modified', models.DateTimeField(auto_now=True)), ("lexer", models.CharField(max_length=50)),
('publish_date', models.DateTimeField(null=True, blank=True)), ("code", models.TextField()),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags')), ("meta", models.TextField(null=True, blank=True)),
('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True)), ("description", models.TextField(null=True, blank=True)),
("stylized", models.TextField(null=True, blank=True)),
("stylized_min", models.TextField(null=True, blank=True)),
("embedded", models.TextField(null=True, blank=True)),
(
"line_count",
models.IntegerField(default=None, null=True, blank=True),
),
("key", models.CharField(max_length=100, null=True, blank=True)),
("public", models.BooleanField(default=False)),
("blog_post", models.BooleanField(default=False)),
("views", models.IntegerField(default=0)),
("created", models.DateTimeField(auto_now_add=True)),
("modified", models.DateTimeField(auto_now=True)),
("publish_date", models.DateTimeField(null=True, blank=True)),
(
"tags",
taggit.managers.TaggableManager(
to="taggit.Tag",
through="taggit.TaggedItem",
help_text="A comma-separated list of tags.",
verbose_name="Tags",
),
),
(
"user",
models.ForeignKey(
blank=True, to=settings.AUTH_USER_MODEL, null=True
),
),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='favorite', model_name="favorite",
name='snipt', name="snipt",
field=models.ForeignKey(to='snipts.Snipt'), field=models.ForeignKey(to="snipts.Snipt"),
), ),
migrations.AddField( migrations.AddField(
model_name='favorite', model_name="favorite",
name='user', name="user",
field=models.ForeignKey(to=settings.AUTH_USER_MODEL), field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
), ),
] ]

View File

@ -9,20 +9,28 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('snipts', '0001_initial'), ("snipts", "0001_initial"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='SniptLogEntry', name="SniptLogEntry",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), (
('code', models.TextField()), "id",
('diff', models.TextField()), models.AutoField(
('created', models.DateTimeField(auto_now_add=True)), verbose_name="ID",
('modified', models.DateTimeField(auto_now=True)), serialize=False,
('snipt', models.ForeignKey(to='snipts.Snipt')), auto_created=True,
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), primary_key=True,
),
),
("code", models.TextField()),
("diff", models.TextField()),
("created", models.DateTimeField(auto_now_add=True)),
("modified", models.DateTimeField(auto_now=True)),
("snipt", models.ForeignKey(to="snipts.Snipt")),
("user", models.ForeignKey(to=settings.AUTH_USER_MODEL)),
], ],
), )
] ]

View File

@ -9,13 +9,18 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('snipts', '0002_sniptlogentry'), ("snipts", "0002_sniptlogentry"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='snipt', model_name="snipt",
name='last_user_saved', name="last_user_saved",
field=models.ForeignKey(related_name='last_user_saved', blank=True, to=settings.AUTH_USER_MODEL, null=True), field=models.ForeignKey(
), related_name="last_user_saved",
blank=True,
to=settings.AUTH_USER_MODEL,
null=True,
),
)
] ]

View File

@ -6,19 +6,17 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("snipts", "0003_snipt_last_user_saved")]
('snipts', '0003_snipt_last_user_saved'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='snipt', model_name="snipt", name="secure", field=models.BooleanField(default=False)
name='secure',
field=models.BooleanField(default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='snipt', model_name="snipt",
name='title', name="title",
field=models.CharField(max_length=255, blank=True, null=True, default='Untitled'), field=models.CharField(
max_length=255, blank=True, null=True, default="Untitled"
),
), ),
] ]

View File

@ -9,18 +9,26 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('snipts', '0004_auto_20160512_1058'), ("snipts", "0004_auto_20160512_1058"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='SniptSecureView', name="SniptSecureView",
fields=[ fields=[
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), (
('created', models.DateTimeField(auto_now_add=True)), "id",
('modified', models.DateTimeField(auto_now=True)), models.AutoField(
('snipt', models.ForeignKey(to='snipts.Snipt')), serialize=False,
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), primary_key=True,
auto_created=True,
verbose_name="ID",
),
),
("created", models.DateTimeField(auto_now_add=True)),
("modified", models.DateTimeField(auto_now=True)),
("snipt", models.ForeignKey(to="snipts.Snipt")),
("user", models.ForeignKey(to=settings.AUTH_USER_MODEL)),
], ],
), )
] ]

View File

@ -23,14 +23,15 @@ class Snipt(models.Model):
"""An individual Snipt.""" """An individual Snipt."""
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.DO_NOTHING) user = models.ForeignKey(User, blank=True, null=True, on_delete=models.DO_NOTHING)
last_user_saved = models.ForeignKey(User, last_user_saved = models.ForeignKey(
blank=True, User,
null=True, blank=True,
related_name='last_user_saved', null=True,
on_delete=models.DO_NOTHING) related_name="last_user_saved",
on_delete=models.DO_NOTHING,
)
title = models.CharField(max_length=255, blank=True, null=True, title = models.CharField(max_length=255, blank=True, null=True, default="Untitled")
default='Untitled')
slug = models.SlugField(max_length=255, blank=True) slug = models.SlugField(max_length=255, blank=True)
custom_slug = models.SlugField(max_length=255, blank=True) custom_slug = models.SlugField(max_length=255, blank=True)
tags = TaggableManager() tags = TaggableManager()
@ -61,7 +62,7 @@ class Snipt(models.Model):
diff = difflib.unified_diff(expected, actual) diff = difflib.unified_diff(expected, actual)
return ''.join(diff) return "".join(diff)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Snipt, self).__init__(*args, **kwargs) super(Snipt, self).__init__(*args, **kwargs)
@ -73,92 +74,101 @@ 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 + self.key = hashlib.md5(
str(datetime.datetime.now()) + (
str(random.random())).encode('utf-8')).hexdigest() self.slug + str(datetime.datetime.now()) + str(random.random())
).encode("utf-8")
).hexdigest()
if self.lexer == 'markdown': if self.lexer == "markdown":
self.stylized = markdown(self.code, 'default') self.stylized = markdown(self.code, "default")
# Snippet embeds # Snippet 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" <script type="text/javascript"
src="https://snippets.siftie.com/embed/{}/?snipt"> src="https://snippets.siftie.com/embed/{}/?snipt">
</script> </script>
<div id="snipt-embed-{}"></div>""".format(match, match)) <div id="snipt-embed-{}"></div>""".format(
match, match
),
)
# YouTube embeds # YouTube embeds
for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]', for match in re.findall(
self.stylized): "\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]", self.stylized
self.stylized = self.stylized \ ):
.replace('[[youtube-{}-{}x{}]]'.format( self.stylized = self.stylized.replace(
str(match[0]), "[[youtube-{}-{}x{}]]".format(
str(match[1]), str(match[0]), str(match[1]), str(match[2])
str(match[2])), ),
"""<iframe width="{}" height="{}" """<iframe width="{}" height="{}"
src="https://www.youtube.com/embed/{}" src="https://www.youtube.com/embed/{}"
frameborder="0" allowfullscreen></iframe>""" frameborder="0" allowfullscreen></iframe>""".format(
.format(match[1], match[2], match[0])) match[1], match[2], match[0]
),
)
# Vimeo embeds # Vimeo embeds
for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]', for match in re.findall("\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]", self.stylized):
self.stylized): self.stylized = self.stylized.replace(
self.stylized = self.stylized \ "[[vimeo-{}-{}x{}]]".format(
.replace('[[vimeo-{}-{}x{}]]'.format( str(match[0]), str(match[1]), str(match[2])
str(match[0]), ),
str(match[1]), """<iframe src="https://player.vimeo.com/video/{}"
str(match[2])),
"""<iframe src="https://player.vimeo.com/video/{}"
width="{}" height="{}" frameborder="0" width="{}" height="{}" frameborder="0"
webkitAllowFullScreen mozallowfullscreen webkitAllowFullScreen mozallowfullscreen
allowFullScreen></iframe>""" allowFullScreen></iframe>""".format(
.format(match[0], match[1], match[2])) 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 \ self.stylized = self.stylized.replace(
.replace( "[[tweet-{}]]".format(str(match)),
'[[tweet-{}]]'.format(str(match)), '<div class="embedded-tweet" data-tweet-id="{}"></div>'.format(
'<div class="embedded-tweet" data-tweet-id="{}"></div>' str(match)
.format(str(match))) ),
)
# Parse usernames # Parse usernames
for match in re.findall('@(\w+) ', self.stylized): for match in re.findall("@(\w+) ", self.stylized):
# Try and get the user by username. # Try and get the user by username.
user = get_object_or_None(User, username=match) user = get_object_or_None(User, username=match)
if user: if user:
url = user.profile.get_user_profile_url() url = user.profile.get_user_profile_url()
self.stylized = self.stylized \ self.stylized = self.stylized.replace(
.replace('@{} '.format(str(match)), "@{} ".format(str(match)),
'<a href="{}">@{}</a> '.format(url, match)) '<a href="{}">@{}</a> '.format(url, match),
)
else: else:
self.stylized = highlight(self.code, self.stylized = highlight(
get_lexer_by_name(self.lexer, self.code,
encoding='UTF-8'), get_lexer_by_name(self.lexer, encoding="UTF-8"),
HtmlFormatter(linenos='table', HtmlFormatter(
anchorlinenos=True, linenos="table", anchorlinenos=True, lineanchors="L", linespans="L"
lineanchors='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":
lexer_for_embedded = 'text' lexer_for_embedded = "text"
else: else:
lexer_for_embedded = self.lexer lexer_for_embedded = self.lexer
embedded = highlight(self.code, embedded = highlight(
get_lexer_by_name(lexer_for_embedded, self.code,
encoding='UTF-8'), get_lexer_by_name(lexer_for_embedded, encoding="UTF-8"),
HtmlFormatter( HtmlFormatter(
style='native', style="native",
noclasses=True, noclasses=True,
prestyles=""" prestyles="""
background-color: #1C1C1C; background-color: #1C1C1C;
border-radius: 5px; border-radius: 5px;
color: #D0D0D0; color: #D0D0D0;
@ -169,22 +179,25 @@ class Snipt(models.Model):
padding: 15px; padding: 15px;
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
-moz-border-radius: 5px; -moz-border-radius: 5px;
""")) """,
embedded = (embedded.replace("\\\"", "\\\\\"") ),
.replace('\'', '\\\'') )
.replace("\\", "\\\\") embedded = (
.replace('background: #202020', '')) embedded.replace('\\"', '\\\\"')
.replace("'", "\\'")
.replace("\\", "\\\\")
.replace("background: #202020", "")
)
self.embedded = embedded self.embedded = embedded
snipt = super(Snipt, self).save(*args, **kwargs) snipt = super(Snipt, self).save(*args, **kwargs)
diff = self._unidiff_output(self.original_code or '', self.code) diff = self._unidiff_output(self.original_code or "", self.code)
if (diff != ''): if diff != "":
log_entry = SniptLogEntry(user=self.last_user_saved, log_entry = SniptLogEntry(
snipt=self, user=self.last_user_saved, snipt=self, code=self.code, diff=diff
code=self.code, )
diff=diff)
log_entry.save() log_entry.save()
return snipt return snipt
@ -197,52 +210,52 @@ class Snipt(models.Model):
def get_stylized_min(self): def get_stylized_min(self):
if self.stylized_min is None: if self.stylized_min is None:
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.stylized_min = highlight(
self.code[:1000], self.code[:1000],
get_lexer_by_name(self.lexer, encoding='UTF-8'), get_lexer_by_name(self.lexer, encoding="UTF-8"),
HtmlFormatter(linenos='table', HtmlFormatter(linenos="table", linenospecial=1, lineanchors="line"),
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( return u"http://{}/{}/".format(
self.user.profile.blog_domain.split(' ')[0], self.slug) self.user.profile.blog_domain.split(" ")[0], self.slug
)
else: else:
return u'https://{}.snippets.siftie.com/{}/'.format( return u"https://{}.snippets.siftie.com/{}/".format(
self.user.username.replace('_', '-'), self.slug) 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)
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( return u"/{}/{}/?key={}".format(self.user.username, self.slug, self.key)
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( return u"http://{}/{}/".format(
self.user.profile.blog_domain.split(' ')[0], self.slug) self.user.profile.blog_domain.split(" ")[0], self.slug
)
else: else:
return u'https://{}.snippets.siftie.com/{}/'.format( return u"https://{}.snippets.siftie.com/{}/".format(
self.user.username, self.slug) self.user.username, self.slug
)
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, return u"/{}/{}/?key={}".format(self.user.username, self.slug, self.key)
self.slug,
self.key)
def get_download_url(self): def get_download_url(self):
@ -252,30 +265,30 @@ class Snipt(models.Model):
lexer_obj = None lexer_obj = None
if lexer_obj and lexer_obj.filenames: if lexer_obj and lexer_obj.filenames:
filename = lexer_obj.filenames[0].replace('*', self.slug) filename = lexer_obj.filenames[0].replace("*", self.slug)
else: else:
if self.lexer == 'markdown': if self.lexer == "markdown":
filename = u'{}.md'.format(self.slug) filename = u"{}.md".format(self.slug)
else: else:
filename = u'{}.txt'.format(self.slug) filename = u"{}.txt".format(self.slug)
return u'/download/{}/{}'.format(self.key, filename) return u"/download/{}/{}".format(self.key, filename)
def get_embed_url(self): def get_embed_url(self):
if settings.DEBUG: if settings.DEBUG:
root = 'http://local.snippets.siftie.com' root = "http://local.snippets.siftie.com"
else: else:
root = 'https://snippets.siftie.com' root = "https://snippets.siftie.com"
return '{}/embed/{}/'.format(root, self.key) return "{}/embed/{}/".format(root, self.key)
def get_raw_url(self): def get_raw_url(self):
return '/raw/{}/'.format(self.key) return "/raw/{}/".format(self.key)
@property @property
def sorted_tags(self): def sorted_tags(self):
return self.tags.all().order_by('name') return self.tags.all().order_by("name")
@property @property
def tags_list(self): def tags_list(self):
@ -283,8 +296,8 @@ class Snipt(models.Model):
@property @property
def lexer_name(self): def lexer_name(self):
if self.lexer == 'markdown': if self.lexer == "markdown":
return 'Markdown' return "Markdown"
else: else:
return get_lexer_by_name(self.lexer).name return get_lexer_by_name(self.lexer).name
@ -311,7 +324,7 @@ class SniptLogEntry(models.Model):
@property @property
def snipt_name(self): def snipt_name(self):
return self.snipt.title or 'Untitled' return self.snipt.title or "Untitled"
class SniptSecureView(models.Model): class SniptSecureView(models.Model):
@ -325,7 +338,7 @@ class SniptSecureView(models.Model):
@property @property
def snipt_name(self): def snipt_name(self):
return self.snipt.title or 'Untitled' return self.snipt.title or "Untitled"
class Favorite(models.Model): class Favorite(models.Model):
@ -336,5 +349,4 @@ class Favorite(models.Model):
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, return u"{} favorited by {}".format(self.snipt.title, self.user.username)
self.user.username)

View File

@ -6,15 +6,14 @@ from snipts.models import Snipt
class SniptIndex(indexes.SearchIndex, indexes.Indexable): class SniptIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True) text = indexes.CharField(document=True, use_template=True)
author = indexes.CharField(model_attr='user') author = indexes.CharField(model_attr="user")
pub_date = indexes.DateTimeField(model_attr='created') pub_date = indexes.DateTimeField(model_attr="created")
public = indexes.BooleanField(model_attr='public') public = indexes.BooleanField(model_attr="public")
typ = indexes.CharField(model_attr='lexer') typ = indexes.CharField(model_attr="lexer")
def get_model(self): def get_model(self):
return Snipt return Snipt
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( return self.get_model().objects.filter(created__lte=datetime.datetime.now())
created__lte=datetime.datetime.now())

View File

@ -10,11 +10,11 @@ from templatetag_sugar.register import tag
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):
user = context['request'].user user = context["request"].user
snipt = context['snipt'] snipt = context["snipt"]
is_favorited = False is_favorited = False
@ -27,37 +27,37 @@ def snipt_is_favorited_by_user(context, asvar):
context[asvar] = is_favorited context[asvar] = is_favorited
return '' return ""
@tag(register, []) @tag(register, [])
def snipts_count_for_user(context): def snipts_count_for_user(context):
user = context['request'].user user = context["request"].user
if user.is_authenticated(): if user.is_authenticated():
snipts = Snipt.objects.filter(user=user).values('id').count() snipts = Snipt.objects.filter(user=user).values("id").count()
else: else:
snipts = 0 snipts = 0
return snipts return snipts
@tag(register, [Constant('as'), Variable()]) @tag(register, [Constant("as"), Variable()])
def signup_enabled(context, asvar): def signup_enabled(context, asvar):
context[asvar] = os.environ.get("DISABLE_SIGNUP") != "true" context[asvar] = os.environ.get("DISABLE_SIGNUP") != "true"
return '' return ""
@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 = ""
for i in range(1, line_numbers + 1): for i in range(1, line_numbers + 1):
html = html + '<span class="special">{}</span>'.format(i) html = html + '<span class="special">{}</span>'.format(i)
@ -67,7 +67,7 @@ def generate_line_numbers(context, line_numbers):
@register.filter @register.filter
def md5(string): def md5(string):
return hashlib.md5(string.lower().encode('utf-8')).hexdigest() return hashlib.md5(string.lower().encode("utf-8")).hexdigest()
@register.filter @register.filter

View File

@ -5,107 +5,113 @@ from tastypie.test import ResourceTestCase
class SniptResourceTest(ResourceTestCase): class SniptResourceTest(ResourceTestCase):
fixtures = ['test_entries.json'] fixtures = ["test_entries.json"]
def setUp(self): def setUp(self):
super(SniptResourceTest, self).setUp() super(SniptResourceTest, self).setUp()
# Johnny # Johnny
self.johnny = User.objects.create_user('johnny', 'johnny@siftie.com', self.johnny = User.objects.create_user(
'password') "johnny", "johnny@siftie.com", "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_auth = self.create_apikey(self.johnny, self.johnny.api_key.key)
self.johnny.api_key.key) self.johnny_private = Snipt(
self.johnny_private = Snipt(title='Private snippet for Johnny', title="Private snippet for Johnny",
lexer='text', lexer="text",
public=False, public=False,
user=self.johnny) user=self.johnny,
self.johnny_public = Snipt(title='Public snippet for Johnny', )
lexer='text', self.johnny_public = Snipt(
public=True, title="Public snippet for Johnny",
user=self.johnny) lexer="text",
public=True,
user=self.johnny,
)
self.johnny_private.save() self.johnny_private.save()
self.johnny_public.save() self.johnny_public.save()
# Bob # Bob
self.bob = User.objects.create_user('bob', 'bob@siftie.com', 'password') self.bob = User.objects.create_user("bob", "bob@siftie.com", "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 snippet for Bob', self.bob_private = Snipt(
lexer='text', title="Private snippet for Bob", lexer="text", public=False, user=self.bob
public=False, )
user=self.bob) self.bob_public = Snipt(
self.bob_public = Snipt(title='Public snippet for Bob', title="Public snippet for Bob", lexer="text", public=True, user=self.bob
lexer='text', )
public=True,
user=self.bob)
self.bob_private.save() self.bob_private.save()
self.bob_public.save() self.bob_public.save()
def test_get_private_list(self): def test_get_private_list(self):
resp = self.api_client.get('/api/private/snipt/', format='json', resp = self.api_client.get(
authentication=self.johnny_auth) "/api/private/snipt/", format="json", authentication=self.johnny_auth
)
self.assertHttpOK(resp) self.assertHttpOK(resp)
self.assertValidJSONResponse(resp) self.assertValidJSONResponse(resp)
self.assertEqual(len(self.deserialize(resp)['objects']), 2) self.assertEqual(len(self.deserialize(resp)["objects"]), 2)
def test_get_private_detail(self): def test_get_private_detail(self):
resp = self.api_client.get( resp = self.api_client.get(
'/api/private/snipt/{}/'.format(self.johnny_private.pk), "/api/private/snipt/{}/".format(self.johnny_private.pk),
format='json', format="json",
authentication=self.johnny_auth) authentication=self.johnny_auth,
)
self.assertHttpOK(resp) self.assertHttpOK(resp)
self.assertValidJSONResponse(resp) self.assertValidJSONResponse(resp)
self.assertEqual(self.deserialize(resp)['key'], self.assertEqual(self.deserialize(resp)["key"], self.johnny_private.key)
self.johnny_private.key)
# Unauthenticated request. # Unauthenticated request.
resp = self.api_client.get( resp = self.api_client.get(
'/api/private/snipt/{}/'.format(self.johnny_private.pk), "/api/private/snipt/{}/".format(self.johnny_private.pk), format="json"
format='json') )
self.assertHttpUnauthorized(resp) self.assertHttpUnauthorized(resp)
# Unauthorized request. # Unauthorized request.
resp = self.api_client.get( resp = self.api_client.get(
'/api/private/snipt/{}/'.format(self.johnny_private.pk), "/api/private/snipt/{}/".format(self.johnny_private.pk),
format='json', format="json",
authentication=self.bob_auth) authentication=self.bob_auth,
)
self.assertHttpUnauthorized(resp) self.assertHttpUnauthorized(resp)
def test_post_private_list(self): def test_post_private_list(self):
new_snipt = { new_snipt = {
'title': 'A new private snippet.', "title": "A new private snippet.",
'lexer': 'text', "lexer": "text",
'public': False, "public": False,
} }
resp = self.api_client.post('/api/private/snipt/', resp = self.api_client.post(
data=new_snipt, "/api/private/snipt/",
format='json', data=new_snipt,
authentication=self.johnny_auth) format="json",
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/', resp = self.api_client.get(
format='json', "/api/private/snipt/", format="json", authentication=self.johnny_auth
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)
def test_get_public_list(self): def test_get_public_list(self):
self.assertEqual(Snipt.objects.count(), 4) self.assertEqual(Snipt.objects.count(), 4)
resp = self.api_client.get('/api/public/snipt/', format='json') resp = self.api_client.get("/api/public/snipt/", format="json")
self.assertHttpOK(resp) self.assertHttpOK(resp)
self.assertValidJSONResponse(resp) self.assertValidJSONResponse(resp)
self.assertEqual(len(self.deserialize(resp)['objects']), 2) self.assertEqual(len(self.deserialize(resp)["objects"]), 2)

View File

@ -3,45 +3,36 @@ from snipts import views
urlpatterns = [ urlpatterns = [
url(r'^s/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$', url(
views.redirect_snipt, name='redirect-snipt'), r"^s/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$",
url(r'^(?P<username>[^/]+)/feed/$', views.redirect_snipt,
views.redirect_user_feed, name="redirect-snipt",
name='redirect-feed'), ),
url(r'^public/tag/(?P<tag_slug>[^/]+)/feed/$', 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, views.redirect_public_tag_feed,
name='redirect-public-tag-feed'), name="redirect-public-tag-feed",
url(r'^(?P<username>[^/]+)/tag/(?P<tag_slug>[^/]+)/feed/$', ),
url(
r"^(?P<username>[^/]+)/tag/(?P<tag_slug>[^/]+)/feed/$",
views.redirect_user_tag_feed, views.redirect_user_tag_feed,
name='redirect-user-tag-feed'), name="redirect-user-tag-feed",
url(r'^public/$', ),
views.list_public, url(r"^public/$", views.list_public, name="list-public"),
name='list-public'), url(
url(r'^public/tag/(?P<tag_slug>[^/]+)/$', r"^public/tag/(?P<tag_slug>[^/]+)/$", views.list_public, name="list-public-tag"
views.list_public, ),
name='list-public-tag'), url(r"^download/(?P<snipt_key>[^/]+).*$", views.download, name="download"),
url(r'^download/(?P<snipt_key>[^/]+).*$', url(r"^embed/(?P<snipt_key>[^/]+)/$", views.embed, name="embed"),
views.download, url(r"^raw/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$", views.raw, name="raw"),
name='download'), url(r"^(?P<username_or_custom_slug>[^/]+)/$", views.list_user, name="list-user"),
url(r'^embed/(?P<snipt_key>[^/]+)/$', url(
views.embed, r"^(?P<username_or_custom_slug>[^/]+)/tag/(?P<tag_slug>[^/]+)/$",
name='embed'),
url(r'^raw/(?P<snipt_key>[^/]+)/(?P<lexer>[^\?]+)?$',
views.raw,
name='raw'),
url(r'^(?P<username_or_custom_slug>[^/]+)/$',
views.list_user, views.list_user,
name='list-user'), name="list-user-tag",
url(r'^(?P<username_or_custom_slug>[^/]+)/tag/(?P<tag_slug>[^/]+)/$', ),
views.list_user, url(r"^(?P<username>[^/]+)/favorites/$", views.favorites, name="favorites"),
name='list-user-tag'), url(r"^(?P<username>[^/]+)/blog-posts/$", views.blog_posts, name="blog-posts"),
url(r'^(?P<username>[^/]+)/favorites/$', url(r"^(?P<username>[^/]+)/(?P<snipt_slug>[^/]+)/$", views.detail, name="detail"),
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

@ -18,12 +18,13 @@ def slugify_uniquely(value, model, slugfield="slug"):
potential = str(suffix) potential = str(suffix)
if not model.objects.filter(**{slugfield: potential}).count(): if not model.objects.filter(**{slugfield: potential}).count():
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 = authenticate(username=request.POST['username'], user = authenticate(
password=request.POST['password1']) username=request.POST["username"], password=request.POST["password1"]
)
login(request, user) login(request, user)
@ -31,12 +32,13 @@ def get_lexers_list():
lexers = list(get_all_lexers()) lexers = list(get_all_lexers())
for l in lexers: for l in lexers:
if l[0] == 'ANTLR With Java Target': if l[0] == "ANTLR With Java Target":
lexers.remove(l) lexers.remove(l)
lexers.append(('Markdown', ('markdown',),)) lexers.append(("Markdown", ("markdown",)))
lexers = sorted(lexers) lexers = sorted(lexers)
return lexers return lexers
user_registered.connect(activate_user) user_registered.connect(activate_user)

View File

@ -7,7 +7,12 @@ from django.core.mail import send_mail
from django.core.paginator import Paginator, InvalidPage from django.core.paginator import Paginator, InvalidPage
from django.db.models import Count from django.db.models import Count
from django.db.models import Q from django.db.models import Q
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest from django.http import (
Http404,
HttpResponse,
HttpResponseRedirect,
HttpResponseBadRequest,
)
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.template import RequestContext from django.template import RequestContext
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache
@ -21,25 +26,25 @@ from teams.models import Team
import os import os
RESULTS_PER_PAGE = getattr(settings, 'HAYSTACK_SEARCH_RESULTS_PER_PAGE', 20) RESULTS_PER_PAGE = getattr(settings, "HAYSTACK_SEARCH_RESULTS_PER_PAGE", 20)
@render_to('snipts/detail.html') @render_to("snipts/detail.html")
def detail(request, username, snipt_slug): def detail(request, username, snipt_slug):
snipt = get_object_or_404(Snipt, user__username=username, slug=snipt_slug) snipt = get_object_or_404(Snipt, user__username=username, slug=snipt_slug)
user = snipt.user user = snipt.user
if snipt.lexer != 'markdown': if snipt.lexer != "markdown":
if 'linenos' not in snipt.stylized: if "linenos" not in snipt.stylized:
snipt.save() snipt.save()
if user != request.user: if user != request.user:
if not snipt.public: if not snipt.public:
if 'key' not in request.GET: if "key" not in request.GET:
raise Http404 raise Http404
else: else:
if request.GET.get('key') != snipt.key: if request.GET.get("key") != snipt.key:
raise Http404 raise Http404
if snipt.secure and not request.user.is_authenticated(): if snipt.secure and not request.user.is_authenticated():
@ -61,35 +66,37 @@ def detail(request, username, snipt_slug):
tags = tags.filter(snipt__user=user, snipt__public=True) tags = tags.filter(snipt__user=user, snipt__public=True)
public = True public = True
tags = tags.annotate(count=Count('taggit_taggeditem_items__id')) tags = tags.annotate(count=Count("taggit_taggeditem_items__id"))
tags = tags.order_by('-count', 'name') tags = tags.order_by("-count", "name")
return { return {
'detail': True, "detail": True,
'has_snipts': True, "has_snipts": True,
'public': public, "public": public,
'snipt': snipt, "snipt": snipt,
'tags': tags, "tags": tags,
'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)
lines = snipt.embedded.split('\n') lines = snipt.embedded.split("\n")
return render(request, return render(
'snipts/embed.html', request,
{'lines': lines, 'snipt': snipt}, "snipts/embed.html",
content_type='application/javascript') {"lines": lines, "snipt": snipt},
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):
if request.blog_user: if request.blog_user:
@ -106,29 +113,28 @@ 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, tags = Tag.objects.filter(snipt__user=user, snipt__public=True).distinct()
snipt__public=True).distinct()
tags = tags.order_by('name') tags = tags.order_by("name")
snipts = snipts.order_by('-created') snipts = snipts.order_by("-created")
context = { context = {
'has_snipts': True, "has_snipts": True,
'public': public, "public": public,
'public_user': public_user, "public_user": public_user,
'snipts': snipts, "snipts": snipts,
'tags': tags, "tags": tags,
'user': user, "user": user,
} }
if 'rss' in request.GET: if "rss" in request.GET:
context['snipts'] = context['snipts'][:20] context["snipts"] = context["snipts"][:20]
return rss(request, context) return rss(request, context)
return context return context
@render_to('snipts/list-user.html') @render_to("snipts/list-user.html")
def favorites(request, username): def favorites(request, username):
if request.user.username != username: if request.user.username != username:
@ -139,39 +145,39 @@ def favorites(request, username):
public = False public = False
favorites = Favorite.objects.filter(user=request.user).values('snipt') favorites = Favorite.objects.filter(user=request.user).values("snipt")
favorites = [f['snipt'] for f in favorites] favorites = [f["snipt"] for f in favorites]
snipts = Snipt.objects.filter(Q(pk__in=favorites)) snipts = Snipt.objects.filter(Q(pk__in=favorites))
tags = Tag.objects.filter(snipt__user=request.user).distinct() tags = Tag.objects.filter(snipt__user=request.user).distinct()
tags = tags.order_by('name') tags = tags.order_by("name")
snipts = snipts.order_by('-created') snipts = snipts.order_by("-created")
context = { context = {
'favorites': favorites, "favorites": favorites,
'has_snipts': True, "has_snipts": True,
'public': public, "public": public,
'public_user': False, "public_user": False,
'snipts': snipts, "snipts": snipts,
'tags': tags, "tags": tags,
'user': request.user, "user": request.user,
} }
if 'rss' in request.GET: if "rss" in request.GET:
context['snipts'] = context['snipts'][:20] context["snipts"] = context["snipts"][:20]
return rss(request, context) return rss(request, context)
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):
if request.blog_user: if request.blog_user:
return blog_list(request) return blog_list(request)
snipts = Snipt.objects.filter(public=True).order_by('-created') snipts = Snipt.objects.filter(public=True).order_by("-created")
if tag_slug: if tag_slug:
snipts = snipts.filter(tags__slug__in=[tag_slug]) snipts = snipts.filter(tags__slug__in=[tag_slug])
@ -179,21 +185,16 @@ def list_public(request, tag_slug=None):
else: else:
tag = None tag = None
context = { context = {"has_snipts": True, "public": True, "snipts": snipts, "tag": tag}
'has_snipts': True,
'public': True,
'snipts': snipts,
'tag': tag,
}
if 'rss' in request.GET: if "rss" in request.GET:
context['snipts'] = context['snipts'][:20] context["snipts"] = context["snipts"][:20]
return rss(request, context) return rss(request, context)
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):
if request.blog_user: if request.blog_user:
@ -208,15 +209,16 @@ 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 \ if (
(request.GET.get('api_key') == user.api_key.key) or \ user == request.user
(user.profile.is_a_team and or (request.GET.get("api_key") == user.api_key.key)
user.team.user_is_member(request.user)): or (user.profile.is_a_team and user.team.user_is_member(request.user))
):
public = False public = False
favorites = Favorite.objects.filter(user=user).values('snipt') favorites = Favorite.objects.filter(user=user).values("snipt")
favorites = [f['snipt'] for f in favorites] favorites = [f["snipt"] for f in favorites]
snipts = snipts.filter(Q(user=user) | Q(pk__in=favorites)) snipts = snipts.filter(Q(user=user) | Q(pk__in=favorites))
tags = tags.filter(snipt__user=user).distinct() tags = tags.filter(snipt__user=user).distinct()
@ -226,8 +228,8 @@ def list_user(request, username_or_custom_slug, tag_slug=None):
snipts = snipts.filter(user=user, public=True) snipts = snipts.filter(user=user, public=True)
public = True public = True
tags = tags.order_by('name') tags = tags.order_by("name")
snipts = snipts.order_by('-created') snipts = snipts.order_by("-created")
if tag_slug: if tag_slug:
snipts = snipts.filter(tags__slug__in=[tag_slug]) snipts = snipts.filter(tags__slug__in=[tag_slug])
@ -236,21 +238,21 @@ def list_user(request, username_or_custom_slug, tag_slug=None):
tag = None tag = None
if tag is None: if tag is None:
snipts = snipts.exclude(tags__name__in=['tmp']) snipts = snipts.exclude(tags__name__in=["tmp"])
context = { context = {
'has_snipts': True, "has_snipts": True,
'public': public, "public": public,
'public_user': (public and user), "public_user": (public and user),
'snipts': snipts, "snipts": snipts,
'tags': tags, "tags": tags,
'tag': tag, "tag": tag,
'user': user, "user": user,
'users_for_full_page': ['robertbanh'], "users_for_full_page": ["robertbanh"],
} }
if 'rss' in request.GET: if "rss" in request.GET:
context['snipts'] = context['snipts'][:20] context["snipts"] = context["snipts"][:20]
return rss(request, context) return rss(request, context)
return context return context
@ -261,7 +263,7 @@ def raw(request, snipt_key, lexer=None):
if request.user == snipt.user: if request.user == snipt.user:
if lexer: if lexer:
lexer = lexer.strip('/') lexer = lexer.strip("/")
if lexer != snipt.lexer: if lexer != snipt.lexer:
@ -274,67 +276,73 @@ 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(request, return render(
'snipts/raw.html', request, "snipts/raw.html", {"snipt": snipt}, content_type=content_type
{'snipt': snipt}, )
content_type=content_type)
def rss(request, context): def rss(request, context):
return render(request, return render(request, "rss.xml", context, content_type="application/rss+xml")
'rss.xml',
context,
content_type="application/rss+xml")
@never_cache @never_cache
def search(request, template='search/search.html', load_all=True, def search(
form_class=ModelSearchForm, searchqueryset=None, request,
context_class=RequestContext, extra_context=None, template="search/search.html",
results_per_page=None): load_all=True,
form_class=ModelSearchForm,
searchqueryset=None,
context_class=RequestContext,
extra_context=None,
results_per_page=None,
):
query = '' query = ""
results = EmptySearchQuerySet() results = EmptySearchQuerySet()
if request.GET.get('q'): if request.GET.get("q"):
searchqueryset = SearchQuerySet() \ searchqueryset = (
.filter(Q(public=True) | Q(author=request.user)) \ SearchQuerySet()
.order_by('-pub_date') .filter(Q(public=True) | Q(author=request.user))
.order_by("-pub_date")
)
if request.user.is_authenticated() and \ if request.user.is_authenticated() and "mine-only" in request.GET:
'mine-only' in request.GET: searchqueryset = (
searchqueryset = SearchQuerySet().filter(author=request.user) \ SearchQuerySet().filter(author=request.user).order_by("-pub_date")
.order_by('-pub_date') )
elif request.user.is_authenticated() and \ elif request.user.is_authenticated() and (
('author' in request.GET and "author" in request.GET and request.GET.get("author")
request.GET.get('author')): ):
author = request.GET.get('author') author = request.GET.get("author")
if author == request.user.username: if author == request.user.username:
searchqueryset = SearchQuerySet().filter(author=request.user) \ searchqueryset = (
.order_by('-pub_date') SearchQuerySet().filter(author=request.user).order_by("-pub_date")
)
else: else:
team = get_object_or_None(Team, slug=author) team = get_object_or_None(Team, slug=author)
if team and team.user_is_member(request.user): if team and team.user_is_member(request.user):
searchqueryset = SearchQuerySet().filter(author=team) \ searchqueryset = (
.order_by('-pub_date') SearchQuerySet().filter(author=team).order_by("-pub_date")
)
form = ModelSearchForm(request.GET, form = ModelSearchForm(
searchqueryset=searchqueryset, request.GET, searchqueryset=searchqueryset, load_all=load_all
load_all=load_all) )
if form.is_valid(): if form.is_valid():
query = form.cleaned_data['q'] query = form.cleaned_data["q"]
results = form.search() results = form.search()
else: else:
form = form_class(searchqueryset=searchqueryset, load_all=load_all) form = form_class(searchqueryset=searchqueryset, load_all=load_all)
@ -342,21 +350,21 @@ def search(request, template='search/search.html', load_all=True,
paginator = Paginator(results, results_per_page or RESULTS_PER_PAGE) paginator = Paginator(results, results_per_page or RESULTS_PER_PAGE)
try: try:
page = paginator.page(int(request.GET.get('page', 1))) page = paginator.page(int(request.GET.get("page", 1)))
except InvalidPage: except InvalidPage:
raise Http404("No such page of results!") raise Http404("No such page of results!")
context = { context = {
'form': form, "form": form,
'has_snipts': True, "has_snipts": True,
'page': page, "page": page,
'paginator': paginator, "paginator": paginator,
'query': query, "query": query,
'suggestion': None, "suggestion": None,
} }
if results.query.backend.include_spelling: if results.query.backend.include_spelling:
context['suggestion'] = form.get_suggestion() context["suggestion"] = form.get_suggestion()
if extra_context: if extra_context:
context.update(extra_context) context.update(extra_context)
@ -370,13 +378,13 @@ def redirect_snipt(request, snipt_key, lexer=None):
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

@ -3,7 +3,8 @@ from teams.models import Team
class TeamAdmin(admin.ModelAdmin): class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'owner', 'created', 'modified') list_display = ("name", "owner", "created", "modified")
ordering = ('-created',) ordering = ("-created",)
admin.site.register(Team, TeamAdmin) admin.site.register(Team, TeamAdmin)

View File

@ -7,22 +7,38 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Team', name="Team",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), (
('name', models.CharField(max_length=255)), "id",
('slug', models.SlugField(max_length=255, blank=True)), models.AutoField(
('created', models.DateTimeField(auto_now_add=True)), verbose_name="ID",
('modified', models.DateTimeField(auto_now=True)), serialize=False,
('members', models.ManyToManyField(related_name='member', to=settings.AUTH_USER_MODEL)), auto_created=True,
('owner', models.ForeignKey(related_name='owner', to=settings.AUTH_USER_MODEL)), primary_key=True,
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), ),
),
("name", models.CharField(max_length=255)),
("slug", models.SlugField(max_length=255, blank=True)),
("created", models.DateTimeField(auto_now_add=True)),
("modified", models.DateTimeField(auto_now=True)),
(
"members",
models.ManyToManyField(
related_name="member", to=settings.AUTH_USER_MODEL
),
),
(
"owner",
models.ForeignKey(
related_name="owner", to=settings.AUTH_USER_MODEL
),
),
("user", models.OneToOneField(to=settings.AUTH_USER_MODEL)),
], ],
), )
] ]

View File

@ -6,15 +6,13 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0001_initial")]
('teams', '0001_initial'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='team', model_name="team",
name='email', name="email",
field=models.EmailField(default='nick@siftie.com', max_length=255), field=models.EmailField(default="nick@siftie.com", max_length=255),
preserve_default=False, preserve_default=False,
), )
] ]

View File

@ -7,14 +7,14 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0002_team_email")]
('teams', '0002_team_email'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='team', model_name="team",
name='user', name="user",
field=models.OneToOneField(null=True, blank=True, to=settings.AUTH_USER_MODEL), field=models.OneToOneField(
), null=True, blank=True, to=settings.AUTH_USER_MODEL
),
)
] ]

View File

@ -7,14 +7,14 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0003_auto_20150818_0057")]
('teams', '0003_auto_20150818_0057'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='team', model_name="team",
name='members', name="members",
field=models.ManyToManyField(related_name='member', to=settings.AUTH_USER_MODEL, blank=True), field=models.ManyToManyField(
), related_name="member", to=settings.AUTH_USER_MODEL, blank=True
),
)
] ]

View File

@ -6,19 +6,15 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0004_auto_20150930_1526")]
('teams', '0004_auto_20150930_1526'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='team', model_name="team",
name='stripe_id', name="stripe_id",
field=models.CharField(max_length=100, null=True, blank=True), field=models.CharField(max_length=100, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='team', model_name="team", name="name", field=models.CharField(max_length=30)
name='name',
field=models.CharField(max_length=30),
), ),
] ]

View File

@ -6,14 +6,25 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0005_auto_20150930_2124")]
('teams', '0005_auto_20150930_2124'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='team', model_name="team",
name='plan', name="plan",
field=models.CharField(default=b'snipt-teams-25-monthly', max_length=100, choices=[(b'snipt-teams-25-monthly', b'25 users, monthly'), (b'snipt-teams-100-monthly', b'100 users, monthly'), (b'snipt-teams-250-monthly', b'250 users, monthly'), (b'snipt-teams-unlimited-monthly', b'Unlimited users, monthly'), (b'snipt-teams-25-yearly', b'25 users, yearly'), (b'snipt-teams-100-yearly', b'100 users, yearly'), (b'snipt-teams-250-yearly', b'250 users, yearly'), (b'snipt-teams-unlimited-yearly', b'Unlimited users, yearly')]), field=models.CharField(
), default=b"snipt-teams-25-monthly",
max_length=100,
choices=[
(b"snipt-teams-25-monthly", b"25 users, monthly"),
(b"snipt-teams-100-monthly", b"100 users, monthly"),
(b"snipt-teams-250-monthly", b"250 users, monthly"),
(b"snipt-teams-unlimited-monthly", b"Unlimited users, monthly"),
(b"snipt-teams-25-yearly", b"25 users, yearly"),
(b"snipt-teams-100-yearly", b"100 users, yearly"),
(b"snipt-teams-250-yearly", b"250 users, yearly"),
(b"snipt-teams-unlimited-yearly", b"Unlimited users, yearly"),
],
),
)
] ]

View File

@ -6,14 +6,10 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0006_team_plan")]
('teams', '0006_team_plan'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='team', model_name="team", name="disabled", field=models.BooleanField(default=False)
name='disabled', )
field=models.BooleanField(default=False),
),
] ]

View File

@ -6,14 +6,27 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0007_team_disabled")]
('teams', '0007_team_disabled'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='team', model_name="team",
name='plan', name="plan",
field=models.CharField(default=b'snipt-teams-25-monthly', max_length=100, null=True, blank=True, choices=[(b'snipt-teams-25-monthly', b'25 users, monthly'), (b'snipt-teams-100-monthly', b'100 users, monthly'), (b'snipt-teams-250-monthly', b'250 users, monthly'), (b'snipt-teams-unlimited-monthly', b'Unlimited users, monthly'), (b'snipt-teams-25-yearly', b'25 users, yearly'), (b'snipt-teams-100-yearly', b'100 users, yearly'), (b'snipt-teams-250-yearly', b'250 users, yearly'), (b'snipt-teams-unlimited-yearly', b'Unlimited users, yearly')]), field=models.CharField(
), default=b"snipt-teams-25-monthly",
max_length=100,
null=True,
blank=True,
choices=[
(b"snipt-teams-25-monthly", b"25 users, monthly"),
(b"snipt-teams-100-monthly", b"100 users, monthly"),
(b"snipt-teams-250-monthly", b"250 users, monthly"),
(b"snipt-teams-unlimited-monthly", b"Unlimited users, monthly"),
(b"snipt-teams-25-yearly", b"25 users, yearly"),
(b"snipt-teams-100-yearly", b"100 users, yearly"),
(b"snipt-teams-250-yearly", b"250 users, yearly"),
(b"snipt-teams-unlimited-yearly", b"Unlimited users, yearly"),
],
),
)
] ]

View File

@ -6,14 +6,27 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("teams", "0008_auto_20151018_2053")]
('teams', '0008_auto_20151018_2053'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='team', model_name="team",
name='plan', name="plan",
field=models.CharField(max_length=100, choices=[('snipt-teams-25-monthly', '25 users, monthly'), ('snipt-teams-100-monthly', '100 users, monthly'), ('snipt-teams-250-monthly', '250 users, monthly'), ('snipt-teams-unlimited-monthly', 'Unlimited users, monthly'), ('snipt-teams-25-yearly', '25 users, yearly'), ('snipt-teams-100-yearly', '100 users, yearly'), ('snipt-teams-250-yearly', '250 users, yearly'), ('snipt-teams-unlimited-yearly', 'Unlimited users, yearly')], blank=True, null=True, default='snipt-teams-25-monthly'), field=models.CharField(
), max_length=100,
choices=[
("snipt-teams-25-monthly", "25 users, monthly"),
("snipt-teams-100-monthly", "100 users, monthly"),
("snipt-teams-250-monthly", "250 users, monthly"),
("snipt-teams-unlimited-monthly", "Unlimited users, monthly"),
("snipt-teams-25-yearly", "25 users, yearly"),
("snipt-teams-100-yearly", "100 users, yearly"),
("snipt-teams-250-yearly", "250 users, yearly"),
("snipt-teams-unlimited-yearly", "Unlimited users, yearly"),
],
blank=True,
null=True,
default="snipt-teams-25-monthly",
),
)
] ]

View File

@ -6,25 +6,32 @@ from snipts.utils import slugify_uniquely
class Team(models.Model): class Team(models.Model):
PLANS = ( PLANS = (
('snipt-teams-25-monthly', '25 users, monthly'), ("snipt-teams-25-monthly", "25 users, monthly"),
('snipt-teams-100-monthly', '100 users, monthly'), ("snipt-teams-100-monthly", "100 users, monthly"),
('snipt-teams-250-monthly', '250 users, monthly'), ("snipt-teams-250-monthly", "250 users, monthly"),
('snipt-teams-unlimited-monthly', 'Unlimited users, monthly'), ("snipt-teams-unlimited-monthly", "Unlimited users, monthly"),
('snipt-teams-25-yearly', '25 users, yearly'), ("snipt-teams-25-yearly", "25 users, yearly"),
('snipt-teams-100-yearly', '100 users, yearly'), ("snipt-teams-100-yearly", "100 users, yearly"),
('snipt-teams-250-yearly', '250 users, yearly'), ("snipt-teams-250-yearly", "250 users, yearly"),
('snipt-teams-unlimited-yearly', 'Unlimited users, yearly'), ("snipt-teams-unlimited-yearly", "Unlimited users, yearly"),
) )
email = models.EmailField(max_length=255) email = models.EmailField(max_length=255)
members = models.ManyToManyField(User, related_name='member', blank=True) members = models.ManyToManyField(User, related_name="member", blank=True)
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
owner = models.ForeignKey(User, related_name='owner', on_delete=models.DO_NOTHING) owner = models.ForeignKey(User, related_name="owner", on_delete=models.DO_NOTHING)
slug = models.SlugField(max_length=255, blank=True) slug = models.SlugField(max_length=255, blank=True)
stripe_id = models.CharField(max_length=100, null=True, blank=True) stripe_id = models.CharField(max_length=100, null=True, blank=True)
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.DO_NOTHING) user = models.OneToOneField(
plan = models.CharField(max_length=100, default='snipt-teams-25-monthly', User, blank=True, null=True, on_delete=models.DO_NOTHING
choices=PLANS, blank=True, null=True) )
plan = models.CharField(
max_length=100,
default="snipt-teams-25-monthly",
choices=PLANS,
blank=True,
null=True,
)
disabled = models.BooleanField(default=False) disabled = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True, editable=False) created = models.DateTimeField(auto_now_add=True, editable=False)
@ -32,7 +39,7 @@ class Team(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.slug: if not self.slug:
self.slug = slugify_uniquely(self.name, User, 'username') self.slug = slugify_uniquely(self.name, User, "username")
return super(Team, self).save(*args, **kwargs) return super(Team, self).save(*args, **kwargs)
def __unicode__(self): def __unicode__(self):
@ -49,18 +56,18 @@ class Team(models.Model):
return 0 return 0
plan_map = { plan_map = {
'snipt-teams-25-monthly': 25, "snipt-teams-25-monthly": 25,
'snipt-teams-100-monthly': 100, "snipt-teams-100-monthly": 100,
'snipt-teams-250-monthly': 250, "snipt-teams-250-monthly": 250,
'snipt-teams-unlimited-monthly': float('inf'), "snipt-teams-unlimited-monthly": float("inf"),
'snipt-teams-25-yearly': 25, "snipt-teams-25-yearly": 25,
'snipt-teams-100-yearly': 100, "snipt-teams-100-yearly": 100,
'snipt-teams-250-yearly': 250, "snipt-teams-250-yearly": 250,
'snipt-teams-unlimited-yearly': float('inf') "snipt-teams-unlimited-yearly": float("inf"),
} }
if plan_map[self.plan] == float('inf'): if plan_map[self.plan] == float("inf"):
return 'Unlimited' return "Unlimited"
else: else:
return plan_map[self.plan] return plan_map[self.plan]

View File

@ -3,15 +3,17 @@ from teams import views
urlpatterns = [ urlpatterns = [
url(r'^for-teams/$', views.for_teams), url(r"^for-teams/$", views.for_teams),
url(r'^for-teams/complete/$', views.for_teams_complete), url(r"^for-teams/complete/$", views.for_teams_complete),
url(r'^(?P<username>[^/]+)/members/remove/(?P<member>[^/]+)/$', url(
views.remove_team_member, r"^(?P<username>[^/]+)/members/remove/(?P<member>[^/]+)/$",
name='remove-team-member'), views.remove_team_member,
url(r'^(?P<username>[^/]+)/members/add/(?P<member>[^/]+)/$', name="remove-team-member",
views.add_team_member, ),
name='add-team-member'), url(
url(r'^(?P<username>[^/]+)/members/$', r"^(?P<username>[^/]+)/members/add/(?P<member>[^/]+)/$",
views.team_members, views.add_team_member,
name='team-members') name="add-team-member",
] ),
url(r"^(?P<username>[^/]+)/members/$", views.team_members, name="team-members"),
]

View File

@ -8,7 +8,7 @@ from django.shortcuts import get_object_or_404
from teams.models import Team from teams.models import Team
@render_to('teams/for-teams.html') @render_to("teams/for-teams.html")
def for_teams(request): def for_teams(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
profile = request.user.profile profile = request.user.profile
@ -18,38 +18,34 @@ def for_teams(request):
@login_required @login_required
@render_to('teams/for-teams-complete.html') @render_to("teams/for-teams-complete.html")
def for_teams_complete(request): def for_teams_complete(request):
if request.method == 'POST' and request.user.is_authenticated(): if request.method == "POST" and request.user.is_authenticated():
team = Team(name=request.POST['team-name'], team = Team(
email=request.POST['email'], name=request.POST["team-name"],
owner=request.user) email=request.POST["email"],
owner=request.user,
)
team.save() team.save()
user = User.objects.create_user(team.slug, user = User.objects.create_user(team.slug, team.email, str(uuid.uuid4()))
team.email,
str(uuid.uuid4()))
team.user = user team.user = user
team.save() team.save()
return { return {"team": team}
'team': team
}
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
@login_required @login_required
@render_to('teams/team-members.html') @render_to("teams/team-members.html")
def team_members(request, username): def team_members(request, username):
team = get_object_or_404(Team, slug=username, disabled=False) team = get_object_or_404(Team, slug=username, disabled=False)
if not team.user_is_member(request.user): if not team.user_is_member(request.user):
raise Http404 raise Http404
return { return {"team": team}
'team': team
}
@login_required @login_required
@ -57,15 +53,14 @@ def add_team_member(request, username, member):
team = get_object_or_404(Team, slug=username, disabled=False) team = get_object_or_404(Team, slug=username, disabled=False)
user = get_object_or_404(User, username=member) user = get_object_or_404(User, username=member)
if (team.owner != request.user): if team.owner != request.user:
raise Http404 raise Http404
if ((team.members.all().count() + 1) > team.member_limit): if (team.members.all().count() + 1) > team.member_limit:
return HttpResponseRedirect('/' + team.slug + return HttpResponseRedirect("/" + team.slug + "/members/?limit-reached")
'/members/?limit-reached')
else: else:
team.members.add(user) team.members.add(user)
return HttpResponseRedirect('/' + team.slug + '/members/') return HttpResponseRedirect("/" + team.slug + "/members/")
@login_required @login_required
@ -73,9 +68,9 @@ def remove_team_member(request, username, member):
team = get_object_or_404(Team, slug=username, disabled=False) team = get_object_or_404(Team, slug=username, disabled=False)
user = get_object_or_404(User, username=member) user = get_object_or_404(User, username=member)
if (team.owner != request.user): if team.owner != request.user:
raise Http404 raise Http404
team.members.remove(user) team.members.remove(user)
return HttpResponseRedirect('/' + team.slug + '/members/') return HttpResponseRedirect("/" + team.slug + "/members/")

78
urls.py
View File

@ -6,21 +6,25 @@ from django.contrib.auth.views import login
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.views.static import serve from django.views.static import serve
from snipts.api import (PublicSniptResource, from snipts.api import (
PublicUserResource, PrivateSniptResource, PublicSniptResource,
PrivateFavoriteResource, PrivateUserProfileResource, PublicUserResource,
PrivateUserResource, PublicTagResource) PrivateSniptResource,
PrivateFavoriteResource,
PrivateUserProfileResource,
PrivateUserResource,
PublicTagResource,
)
from snipts.views import search from snipts.views import search
from tastypie.api import Api from tastypie.api import Api
from views import (homepage, lexers, login_redirect, from views import homepage, lexers, login_redirect, tags, user_api_key
tags, user_api_key)
public_api = Api(api_name='public') public_api = Api(api_name="public")
public_api.register(PublicSniptResource()) public_api.register(PublicSniptResource())
public_api.register(PublicTagResource()) public_api.register(PublicTagResource())
public_api.register(PublicUserResource()) public_api.register(PublicUserResource())
private_api = Api(api_name='private') private_api = Api(api_name="private")
private_api.register(PrivateSniptResource()) private_api.register(PrivateSniptResource())
private_api.register(PrivateUserResource()) private_api.register(PrivateUserResource())
private_api.register(PrivateFavoriteResource()) private_api.register(PrivateFavoriteResource())
@ -30,37 +34,39 @@ urlpatterns = []
if os.environ.get("DISABLE_SIGNUP") == "true": if os.environ.get("DISABLE_SIGNUP") == "true":
urlpatterns += [ urlpatterns += [
url(r'^register/?$', lambda x: HttpResponseRedirect('/404/')), url(r"^register/?$", lambda x: HttpResponseRedirect("/404/")),
url(r'^signup/?$', lambda x: HttpResponseRedirect('/404/')), url(r"^signup/?$", lambda x: HttpResponseRedirect("/404/")),
] ]
else: else:
urlpatterns += [ urlpatterns += [url(r"^signup/?$", lambda x: HttpResponseRedirect("/register/"))]
url(r'^signup/?$', lambda x: HttpResponseRedirect('/register/')),
]
urlpatterns += [ urlpatterns += [
url(r'^$', homepage), url(r"^$", homepage),
url(r'', include('registration.backends.simple.urls')), url(r"", include("registration.backends.simple.urls")),
url(r'^login/?$', login, name='login'), url(r"^login/?$", login, name="login"),
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$", TemplateView.as_view(template_name="robots.txt")),
url(r'^tags/$', tags), url(r"^tags/$", tags),
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'^', include('teams.urls')), url(r"^", include("teams.urls")),
url(r'^', include('snipts.urls')), url(r"^", include("snipts.urls")),
url(r'^(?P<path>favicon\.ico)$', serve, { url(
'document_root': os.path.join(os.path.dirname(__file__), 'static/img') r"^(?P<path>favicon\.ico)$",
}), serve,
url(r'^static/(?P<path>.*)$', serve, { {"document_root": os.path.join(os.path.dirname(__file__), "static/img")},
'document_root': os.path.join(os.path.dirname(__file__), 'media') ),
}) url(
r"^static/(?P<path>.*)$",
serve,
{"document_root": os.path.join(os.path.dirname(__file__), "media")},
),
] ]

View File

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

View File

@ -3,10 +3,10 @@ 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:
kwargs = {'email': username} kwargs = {"email": username}
else: else:
kwargs = {'username': username} kwargs = {"username": username}
try: try:
user = User.objects.get(**kwargs) user = User.objects.get(**kwargs)
if user.check_password(password): if user.check_password(password):

View File

@ -9,34 +9,36 @@ 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( existing = User.objects.filter(username__iexact=self.cleaned_data["username"])
username__iexact=self.cleaned_data['username'])
if existing.exists(): if existing.exists():
raise forms.ValidationError( raise forms.ValidationError(_("A user with that username already exists."))
_("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."))
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."))
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."))
else: else:
return self.cleaned_data['username'] return self.cleaned_data["username"]
def clean_email(self): def clean_email(self):
""" """
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( raise forms.ValidationError(
_("""This email address is already in use. Please supply a _(
different email address.""")) """This email address is already in use. Please supply a
return self.cleaned_data['email'] different email address."""
)
)
return self.cleaned_data["email"]

View File

@ -8,11 +8,10 @@ register = template.Library()
@register.filter @register.filter
def pygmentize(text): def pygmentize(text):
return highlight(text, return highlight(
get_lexer_by_name('diff', text,
encoding='UTF-8'), get_lexer_by_name("diff", encoding="UTF-8"),
HtmlFormatter(linenos='table', HtmlFormatter(
anchorlinenos=True, linenos="table", anchorlinenos=True, lineanchors="L", linespans="L"
lineanchors='L', ),
linespans='L', )
))

View File

@ -5,4 +5,4 @@ register = template.Library()
@register.filter @register.filter
def truncate_lines(text): def truncate_lines(text):
return '\n'.join(text.split('\n')[:300]) return "\n".join(text.split("\n")[:300])

View File

@ -17,7 +17,6 @@ register = template.Library()
class VerbatimNode(template.Node): class VerbatimNode(template.Node):
def __init__(self, text): def __init__(self, text):
self.text = text self.text = text
@ -30,15 +29,15 @@ def verbatim(parser, token):
text = [] text = []
while 1: while 1:
token = parser.tokens.pop(0) token = parser.tokens.pop(0)
if token.contents == 'endverbatim': if token.contents == "endverbatim":
break break
if token.token_type == template.TOKEN_VAR: if token.token_type == template.TOKEN_VAR:
text.append('{{') text.append("{{")
elif token.token_type == template.TOKEN_BLOCK: elif token.token_type == template.TOKEN_BLOCK:
text.append('{%') text.append("{%")
text.append(token.contents) text.append(token.contents)
if token.token_type == template.TOKEN_VAR: if token.token_type == template.TOKEN_VAR:
text.append('}}') text.append("}}")
elif token.token_type == template.TOKEN_BLOCK: elif token.token_type == template.TOKEN_BLOCK:
text.append('%}') text.append("%}")
return VerbatimNode(''.join(text)) return VerbatimNode("".join(text))

View File

@ -6,10 +6,11 @@ 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
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
return super(RegistrationView, self).dispatch(request, *args, **kwargs) return super(RegistrationView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, request): def get_success_url(self, request):
return '/login-redirect' return "/login-redirect"

View File

@ -6,7 +6,7 @@ from snipts.utils import get_lexers_list
from taggit.models import Tag from taggit.models import Tag
@render_to('homepage.html') @render_to("homepage.html")
def homepage(request): def homepage(request):
if request.blog_user: if request.blog_user:
@ -32,39 +32,32 @@ def lexers(request):
except IndexError: except IndexError:
mimetypes = [] mimetypes = []
objects.append({ objects.append(
'name': l[0], {"name": l[0], "lexers": l[1], "filters": filters, "mimetypes": mimetypes}
'lexers': l[1], )
'filters': filters,
'mimetypes': mimetypes
})
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("/")
@render_to('tags.html') @render_to("tags.html")
def tags(request): def tags(request):
all_tags = Tag.objects.filter(snipt__public=True).order_by('name') all_tags = Tag.objects.filter(snipt__public=True).order_by("name")
all_tags = all_tags.annotate(count=Count('taggit_taggeditem_items__id')) all_tags = all_tags.annotate(count=Count("taggit_taggeditem_items__id"))
popular_tags = Tag.objects.filter(snipt__public=True) popular_tags = Tag.objects.filter(snipt__public=True)
popular_tags = popular_tags.annotate( popular_tags = popular_tags.annotate(count=Count("taggit_taggeditem_items__id"))
count=Count('taggit_taggeditem_items__id')) popular_tags = popular_tags.order_by("-count")[:20]
popular_tags = popular_tags.order_by('-count')[:20]
popular_tags = sorted(popular_tags, key=lambda tag: tag.name) popular_tags = sorted(popular_tags, key=lambda tag: tag.name)
return { return {"all_tags": all_tags, "tags": popular_tags}
'all_tags': all_tags,
'tags': popular_tags
}
@ajax_request @ajax_request
@ -73,6 +66,4 @@ def user_api_key(request):
if not request.user.is_authenticated(): if not request.user.is_authenticated():
return HttpResponseBadRequest() return HttpResponseBadRequest()
return { return {"api_key": request.user.api_key.key}
'api_key': request.user.api_key.key
}