Archived
1
0

it now works

This commit is contained in:
Jeff Becker 2016-11-06 17:47:23 -05:00
parent 14c68abf9d
commit f25f2cd956
No known key found for this signature in database
GPG Key ID: AB950234D6EA286B
13 changed files with 159 additions and 23 deletions

View File

@ -62,6 +62,12 @@ class Post(models.Model):
placeholder = models.BooleanField(default=False)
last_bumped = models.IntegerField(default=0)
def has_attachment(self, filehash):
for att in self.attachments.all():
if att.filehash == filehash:
return True
return False
def get_all_replies(self):
if self.is_op():
return Post.objects.filter(reference=self.msgid).order_by('posted')

View File

@ -121,7 +121,8 @@ form {
}
.frontpage.posts {
.posts {
flex-direction: column;
width: 50%;
padding-top: 10%;
padding-left: 25%;

View File

@ -8,7 +8,8 @@ urlpatterns = [
url(r'^overchan\.(?P<name>[a-zA-Z0-9\.]+)-(?P<page>[0-9]+)\.html$', views.BoardView.as_view(), name='old-board'),
url(r'^overchan\.(?P<name>[a-zA-Z0-9\.]+)/', views.BoardView.as_view(), name='board-alt'),
url(r'^thread-(?P<op>[a-fA-F0-9\.]{40})\.html$', views.ThreadView.as_view(), name='old-thread'),
url(r'^b/(?P<name>[a-zA-Z0-9]+)/$', views.BoardView.as_view(), name='board'),
url(r'^b/(?P<name>[a-zA-Z0-9\.]+[a-zA-Z0-9])/$', views.BoardView.as_view(), name='board'),
url(r'^t/(?P<op>[a-fA-F0-9\.]{40})/$', views.ThreadView.as_view(), name='thread'),
url(r'captcha.png', views.create_captcha, name='captcha'),
url(r'^$', views.FrontPageView.as_view(), name='index'),
]

View File

@ -1,8 +1,15 @@
from django.conf import settings
import base64
import hashlib
import re
from datetime import datetime
import time
import string
import random
import nntplib
import email.message
def hashid(msgid):
h = hashlib.sha1()
@ -24,3 +31,50 @@ def msgid_valid(msgid):
def time_int(dtime):
return time.mktime(dtime.timetuple())
def randstr(l, base=string.digits):
r = ''
while l > 0:
r += random.choice(base)
l -= 1
return r
def createPost(newsgroup, ref, form, files):
"""
create a post and post it to a news server
"""
msg = email.message.Message()
msg['Content-Type'] = 'multipart/mixed'
msg["Subject"] = form["subject"] or "None"
msg['Date'] = email.utils.format_datetime(datetime.now())
msg['Message-ID'] = email.utils.make_msgid(randstr(13), settings.FRONTEND_NAME)
if not msgid_valid(ref):
return None, "invalid reference: {}".format(ref)
msg["References"] = ref
msg["Newsgroups"] = newsgroup
msg["From"] = '{} <anon@django.nntpchan.tld>'.format(form['name'] or 'Anonymous')
if 'attachment' in files:
f = files['attachment']
part = email.message.Message()
part['Content-Type'] = f.content_type
part['Content-Disposition'] = 'form-data; filename="{}"; name="attachment"'.format(f.name)
part['Content-Transfer-Encoding'] = 'base64'
part.set_payload(base64.b64encode(f.read()))
msg.attach(part)
text = email.message.Message()
m = '{}'.format(form['message'] or ' ')
text.set_payload(m)
text['Content-Type'] = 'text/plain'
msg.attach(text)
server = settings.NNTP_SERVER
server['readermode'] = True
msgid = None
try:
with nntplib.NNTP(**server) as nntp:
nntp.login(**settings.NNTP_LOGIN)
msgid = nntp.post(msg.as_bytes())
except Exception as e:
return None, e
return msgid, None

View File

@ -1,3 +1,4 @@
from django.conf import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponse, Http404
from django.shortcuts import render, get_object_or_404
@ -6,18 +7,40 @@ from django.views import generic
from .models import Post, Newsgroup
from captcha.image import ImageCaptcha
from . import util
captcha = ImageCaptcha(fonts=settings.CAPTCHA_FONTS)
class Postable:
"""
postable view
checks captcha etc
"""
def context_for_get(self, request, defaults):
defaults['captcha'] = reverse('captcha')
defaults['refresh_url'] = request.path
return defaults
def handle_post(self, request, **kwargs):
"""
handle post request, implement in subclass
"""
return None, 'handle_post() not implemented'
def post(self, request, **kwargs):
ctx = {
'error' : None
'error' : 'invalid captcha'
}
solution = request.session['captcha']
if solution is not None:
if 'captcha' in request.POST:
if request.POST['captcha'].lower() == solution.lower():
ctx['msgid'], ctx['error'] = self.handle_post(request, **kwargs)
request.session['captcha'] = ''
request.session.save()
return render(request, 'frontend/postresult.html', ctx)
@ -27,6 +50,16 @@ class BoardView(generic.View, Postable):
context_object_name = 'threads'
model = Post
def handle_post(self, request, name):
"""
make a new thread
"""
name = 'overchan.{}'.format(name)
if not util.newsgroup_valid(name):
return None, "invalid newsgroup: {}".format(name)
return util.createPost(name, '', request.POST, request.FILES)
def get(self, request, name):
page = 0
if 'p' in request.GET:
@ -46,20 +79,32 @@ class BoardView(generic.View, Postable):
begin = page * group.posts_per_page
end = begin + group.posts_per_page - 1
roots = self.model.objects.filter(newsgroup=group, reference='').order_by('-last_bumped')[begin:end]
ctx = {'threads': roots, 'page': page, 'name': newsgroup}
ctx = self.context_for_get(request, {'threads': roots, 'page': page, 'name': newsgroup})
if page < group.max_pages:
ctx['nextpage'] = reverse('board', args=[name]) + '?p={}'.format(page + 1)
if page > 0:
ctx['prevpage'] = reverse('board', args=[name]) + '?p={}'.format(page - 1)
return render(request, self.template_name, ctx)
class ThreadView(generic.ListView, Postable):
class ThreadView(generic.View, Postable):
template_name = 'frontend/thread.html'
model = Post
context_object_name = 'op'
def get_queryset(self):
return get_object_or_404(self.model, posthash=self.kwargs['op'])
def handle_post(self, request, op):
"""
make a new thread
"""
post = get_object_or_404(self.model, posthash=op)
name = post.newsgroup.name
if not util.newsgroup_valid(name):
return None, "invalid newsgroup: {}".format(name)
return util.createPost(name, post.msgid, request.POST, request.FILES)
def get(self, request, op):
posts = get_object_or_404(self.model, posthash=op)
ctx = self.context_for_get(request, {'op': posts})
return render(request, self.template_name, ctx)
class FrontPageView(generic.View):
template_name = 'frontend/frontpage.html'
@ -77,3 +122,12 @@ def modlog(request, page):
if page is None:
page = 0
return HttpResponse('mod log page {}'.format(page))
def create_captcha(request):
solution = util.randstr(7).lower()
request.session['captcha'] = solution
request.session.save()
c = captcha.generate(solution)
r =HttpResponse(c)
r['Content-Type'] = 'image/png'
return r

View File

@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/1.10/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.10/ref/settings/
"""
import glob
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@ -133,3 +133,23 @@ MEDIA_URL = '/media/'
# for thumbnailing
CONVERT_PATH = '/usr/bin/convert'
FFMPEG_PATH = '/usr/bin/ffmpeg'
# for captcha
CAPTCHA_FONT_DIR = os.path.join(ASSETS_ROOT, 'fonts')
CAPTCHA_FONTS = glob.glob(os.path.join(CAPTCHA_FONT_DIR, '*.ttf'))
# for nntp server
# see nntplib module for more info
NNTP_SERVER = {
'host': '127.0.0.1',
'port': 1129
}
# nntp server login credentials
NNTP_LOGIN = {
'user': None,
'password': None
}
FRONTEND_NAME = 'ebin.tld'

View File

@ -8,6 +8,7 @@
<script type="text/javascript" src="{% static 'postform.js' %}"></script>
<script type="text/javascript" src="{% static 'banners.js' %}"></script>
<script type="text/javascript" src="{% static 'settings.js' %}"></script>
{% block head %}{% endblock %}
</head>
<body>
<div id="wrapper">

View File

@ -1,6 +1,6 @@
{% load captcha %}
<div class="postform">
<form id="postform" method="post" action="">
<form id="postform" method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
<table class="postform">
<tbody>
@ -33,15 +33,7 @@
<th>
</th>
<td>
<input class="postform_attachment" id="postform_attachments" type="file" name="attachment_uploaded" multiple />
</td>
</tr>
<tr>
<th>
</th>
<td>
<input type="checkbox" name="dubs" />
<input class="postform_attachment" id="postform_attachments" type="file" name="attachment" multiple />
</td>
</tr>
<tr>

View File

@ -1,9 +1,12 @@
{% extends "frontend/base.html" %}
{% block head %}
<meta http-equiv="refresh" content="2; {{refresh_url}}"></meta>
{% endblock %}
{% block content %}
{% if error %}
<pre class="error"> failed to post {{error}} </pre>
<pre class="error"> failed to post: {{error}} </pre>
{% else %}
{% if msgid %}
<pre class="posted"> posted as {{msgid}} </pre>

View File

@ -5,6 +5,7 @@
<span class="navbar_link"><a href="{{op.newsgroup.get_absolute_url}}">back to {{op.newsgroup.name}}</a></span>
{% endblock %}
{% block content %}
{% include "frontend/postform.html" %}
<div id="{{op.posthash}}" class="thread">
<div class="post op">
<div class="header">

View File

@ -105,6 +105,8 @@ def webhook(request):
post.save()
for att in atts:
if post.has_attachment(att.filehash):
continue
post.attachments.add(att)

View File

@ -1,2 +1,3 @@
captcha
django
psycopg2