Archived
1
0

various fixes

This commit is contained in:
Jeff Becker 2016-11-06 10:20:03 -05:00
parent cc957f3de5
commit a9860d82ba
No known key found for this signature in database
GPG Key ID: AB950234D6EA286B
13 changed files with 182 additions and 49 deletions

View File

@ -13,10 +13,10 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Attachment', name='Attachment',
fields=[ fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('filehash', models.CharField(editable=False, max_length=256)), ('filehash', models.CharField(editable=False, max_length=256)),
('filename', models.CharField(max_length=256)), ('filename', models.CharField(max_length=256)),
('mimetype', models.CharField(max_length=256, default='text/plain')), ('mimetype', models.CharField(default='text/plain', max_length=256)),
('width', models.IntegerField(default=0)), ('width', models.IntegerField(default=0)),
('height', models.IntegerField(default=0)), ('height', models.IntegerField(default=0)),
('banned', models.BooleanField(default=False)), ('banned', models.BooleanField(default=False)),
@ -25,7 +25,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Newsgroup', name='Newsgroup',
fields=[ fields=[
('name', models.CharField(editable=False, max_length=256, serialize=False, primary_key=True)), ('name', models.CharField(primary_key=True, serialize=False, editable=False, max_length=256)),
('posts_per_page', models.IntegerField(default=10)), ('posts_per_page', models.IntegerField(default=10)),
('max_pages', models.IntegerField(default=10)), ('max_pages', models.IntegerField(default=10)),
('banned', models.BooleanField(default=False)), ('banned', models.BooleanField(default=False)),
@ -34,16 +34,17 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Post', name='Post',
fields=[ fields=[
('msgid', models.CharField(editable=False, max_length=256, serialize=False, primary_key=True)), ('msgid', models.CharField(primary_key=True, serialize=False, editable=False, max_length=256)),
('posthash', models.CharField(editable=False, max_length=256)), ('posthash', models.CharField(editable=False, max_length=256)),
('reference', models.CharField(max_length=256, default='')), ('reference', models.CharField(default='', max_length=256)),
('message', models.TextField(default='')), ('message', models.TextField(default='')),
('subject', models.CharField(max_length=256, default='None')), ('subject', models.CharField(default='None', max_length=256)),
('name', models.CharField(max_length=256, default='Anonymous')), ('name', models.CharField(default='Anonymous', max_length=256)),
('pubkey', models.CharField(max_length=64, default='')), ('pubkey', models.CharField(default='', max_length=64)),
('signature', models.CharField(max_length=64, default='')), ('signature', models.CharField(default='', max_length=64)),
('posted', models.DateTimeField()), ('posted', models.IntegerField(default=0)),
('placeholder', models.BooleanField(default=False)), ('placeholder', models.BooleanField(default=False)),
('last_bumped', models.IntegerField(default=0)),
('attachments', models.ManyToManyField(to='frontend.Attachment')), ('attachments', models.ManyToManyField(to='frontend.Attachment')),
('newsgroup', models.ForeignKey(to='frontend.Newsgroup')), ('newsgroup', models.ForeignKey(to='frontend.Newsgroup')),
], ],

View File

@ -1,8 +1,11 @@
from django.db import models from django.db import models
from django.core.urlresolvers import reverse
from . import util from . import util
import mimetypes import mimetypes
from datetime import datetime
class Attachment(models.Model): class Attachment(models.Model):
""" """
@ -37,12 +40,11 @@ class Newsgroup(models.Model):
banned = models.BooleanField(default=False) banned = models.BooleanField(default=False)
def get_absolute_url(self): def get_absolute_url(self):
from django.urls import reverse return reverse('board', args=[self.name[9:]])
return reverse('nntpchan.frontend.views.boardpage', args=[self.name, '0'])
class Post(models.Model): class Post(models.Model):
""" """
a post made a post made anywhere on the boards
""" """
msgid = models.CharField(max_length=256, primary_key=True, editable=False) msgid = models.CharField(max_length=256, primary_key=True, editable=False)
@ -55,8 +57,9 @@ class Post(models.Model):
signature = models.CharField(max_length=64, default='') signature = models.CharField(max_length=64, default='')
newsgroup = models.ForeignKey(Newsgroup, on_delete=models.CASCADE) newsgroup = models.ForeignKey(Newsgroup, on_delete=models.CASCADE)
attachments = models.ManyToManyField(Attachment) attachments = models.ManyToManyField(Attachment)
posted = models.DateTimeField() posted = models.IntegerField(default=0)
placeholder = models.BooleanField(default=False) placeholder = models.BooleanField(default=False)
last_bumped = models.IntegerField(default=0)
def get_all_replies(self): def get_all_replies(self):
if self.is_op(): if self.is_op():
@ -70,16 +73,23 @@ class Post(models.Model):
return rpls return rpls
def is_op(self): def is_op(self):
return self.reference == '' return self.reference == '' or self.reference == self.msgid
def shorthash(self): def shorthash(self):
return self.posthash[:10] return self.posthash[:10]
def postdate(self):
return datetime.fromtimestamp(self.posted)
def get_absolute_url(self): def get_absolute_url(self):
if self.is_op(): if self.is_op():
op = util.hashid(self.msgid) op = util.hashid(self.msgid)
return '/t/{}/'.format(op) return reverse('thread', args=[op])
else: else:
op = util.hashid(self.reference) op = util.hashid(self.reference)
frag = util.hashid(self.msgid) frag = util.hashid(self.msgid)
return '/t/{}/#{}'.format(op, frag) return reverse('thread', args=[op]) + '#{}'.format(frag)
def bump(self):
if self.is_op():
self.last_bumped = util.time_int(datetime.now())

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

View File

@ -1,6 +1,6 @@
body, html { body, html {
background-color: #eef2ff; background: #EEF2FF url('/static/bg.png') repeat-x 50% 0%;
} }
div { div {
@ -68,6 +68,27 @@ img.thumb {
color: #480188; color: #480188;
} }
.frontpage.posts {
width: 50%;
padding-top: 10%;
padding-left: 25%;
}
footer {
font-size: 8pt;
margin-top: 5%;
maring-bottom: 5%;
padding-left: 1%;
margin: 0px;
min-width: 0px;
bottom: 0px;
}
.paginator {
padding-top: 5%;
padding-bottom: 5%;
}
.cite { .cite {
float: right; float: right;
} }

View File

@ -143,3 +143,10 @@ def memepost(text, autoescape=True):
return mark_safe(doFilter(line_funcs, return_text, conditional_escape)) return mark_safe(doFilter(line_funcs, return_text, conditional_escape))
@register.filter(name='truncate')
@stringfilter
def truncate(text, truncate=500):
if len(text) > truncate:
return text[:truncate] + '...'
return text

View File

@ -2,6 +2,8 @@ import base64
import hashlib import hashlib
import re import re
import time
def hashid(msgid): def hashid(msgid):
h = hashlib.sha1() h = hashlib.sha1()
m = '{}'.format(msgid).encode('ascii') m = '{}'.format(msgid).encode('ascii')
@ -19,8 +21,6 @@ def hashfile(data):
def msgid_valid(msgid): def msgid_valid(msgid):
return re.match("<[a-zA-Z0-9\$\._\-\|]+@[a-zA-Z0-9\$\._\-\|]+>$", msgid) is not None return re.match("<[a-zA-Z0-9\$\._\-\|]+@[a-zA-Z0-9\$\._\-\|]+>$", msgid) is not None
def save_part(part): def time_int(dtime):
""" return time.mktime(dtime.timetuple())
save a mime part to disk
"""

View File

@ -19,8 +19,26 @@ class BoardView(generic.View):
else: else:
begin = page * group.posts_per_page begin = page * group.posts_per_page
end = begin + group.posts_per_page end = begin + group.posts_per_page
posts = self.model.objects.filter(newsgroup=group, reference='').order_by('-posted')[begin:end] print(begin, end)
return render(request, self.template_name, {'threads': posts, 'page': page, 'name': newsgroup}) posts = self.model.objects.filter(newsgroup=group).order_by('-posted')[begin:end]
roots = []
for post in posts:
if post.is_op():
if post not in roots:
roots.append(post)
else:
op = self.model.objects.filter(msgid=post.reference)
if len(op) > 0:
op = op[0]
if op not in roots:
roots.append(op)
ctx = {'threads': roots,'page': page, 'name': newsgroup}
if page < group.max_pages:
ctx['nextpage'] = '/{}/{}/'.format(group.name, page + 1)
if page > 0:
ctx['prevpage'] = '/{}/{}/'.format(group.name, page - 1)
return render(request, self.template_name, ctx)
class ThreadView(generic.ListView): class ThreadView(generic.ListView):
@ -33,12 +51,16 @@ class ThreadView(generic.ListView):
class FrontPageView(generic.ListView): class FrontPageView(generic.View):
template_name = 'frontend/frontpage.html' template_name = 'frontend/frontpage.html'
model = Post model = Post
def get_queryset(self): def get(self, request, truncate=5):
return self.model.objects.order_by('posted')[:10] if truncate <= 0:
truncate = 5
posts = self.model.objects.order_by('-posted')[:truncate]
ctx = {'posts' : posts}
return render(request, self.template_name, ctx)
def modlog(request, page): def modlog(request, page):

View File

@ -111,6 +111,8 @@ LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'
DATE_FORMAT = "r"
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True

View File

@ -10,7 +10,8 @@
<body> <body>
<div id="wrapper"> <div id="wrapper">
<div id="navbar"> <div id="navbar">
<span id="navbar_title">{% block navbar_title %}nntpchan{% endblock %}</span> | <span id="navbar_frontpage"><a href="/">nntpchan</a></span> |
<span id="navbar_title">{% block navbar_title %}{% endblock %}</span> |
<span id="navbar_links">{% block navbar_links %}{% endblock %}</span> | <span id="navbar_links">{% block navbar_links %}{% endblock %}</span> |
<span id="navbar_left">{% block navbar_left %}| <a href="#" onclick="nntpchan_toggle_settings()">settings</a>{% endblock %}</span> <span id="navbar_left">{% block navbar_left %}| <a href="#" onclick="nntpchan_toggle_settings()">settings</a>{% endblock %}</span>
</div> </div>
@ -19,9 +20,13 @@
{% endblock %} {% endblock %}
</div> </div>
</div> </div>
<footer> <hr/>
<p> legal notice goes here </p> <center>
</footer> <footer>
<p>All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p>To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</center>
<script> <script>
ready(); ready();
</script> </script>

View File

@ -2,6 +2,15 @@
{% extends "frontend/base.html" %} {% extends "frontend/base.html" %}
{% load chanup %} {% load chanup %}
{% block title %} {{name}} page {{page}} {% endblock %} {% block title %} {{name}} page {{page}} {% endblock %}
{% block navbar_links %}
{% if prevpage %}
<span class="navbar_link"><a href="{{prevpage}}">previous page</a></span>
{% endif %}
{% if nextpage %}
{% if prevpage %}|{% endif %}
<span class="navbar_link"><a href="{{nextpage}}">next page</a></span>
{% endif %}
{% endblock %}
{% block content %} {% block content %}
<hr /> <hr />
{% for op in threads %} {% for op in threads %}
@ -9,8 +18,7 @@
<div class="post op"> <div class="post op">
<div class="header"> <div class="header">
<span class="name">{{op.name}}</span> <span class="subject">{{op.subject}}</span> <span class="name">{{op.name}}</span> <span class="subject">{{op.subject}}</span>
<span class="msgid">{{op.msgid}}</span> <span class="posted">{{op.postdate|date}}</span>
<span class="posted">{{op.posted}}</span>
<span class="cite"><a href="{{op.get_absolute_url}}">&gt;&gt;{{op.shorthash}}</a></span> <span class="cite"><a href="{{op.get_absolute_url}}">&gt;&gt;{{op.shorthash}}</a></span>
</div> </div>
@ -19,14 +27,13 @@
<a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a> <a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a>
{% endfor %} {% endfor %}
</div> </div>
<pre class="postbody">{{op.message|memepost}}</pre> <pre class="postbody">{{op.message|truncate|memepost}}</pre>
</div> </div>
{% for reply in op.get_board_replies %} {% for reply in op.get_board_replies %}
<div class="post reply"> <div class="post reply" id="{{reply.posthash}}">
<div class="header"> <div class="header">
<span class="name">{{reply.name}}</span> <span class="subject">{{reply.subject}}</span> <span class="name">{{reply.name}}</span> <span class="subject">{{reply.subject}}</span>
<span class="msgid">{{reply.msgid}}</span> <span class="posted">{{reply.postdate|date}}</span>
<span class="posted">{{reply.posted}}</span>
<span class="cite"><a href="{{reply.get_absolute_url}}">&gt;&gt;{{reply.shorthash}}</a></span> <span class="cite"><a href="{{reply.get_absolute_url}}">&gt;&gt;{{reply.shorthash}}</a></span>
</div> </div>
<div class="attachments"> <div class="attachments">
@ -34,10 +41,18 @@
<a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a> <a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a>
{% endfor %} {% endfor %}
</div> </div>
<pre class="postbody">{{reply.message|memepost}}</pre> <pre class="postbody">{{reply.message|truncate|memepost}}</pre>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<hr />
{% endfor %} {% endfor %}
<div class="paginator">
{% if prevpage %}
<span class="paginator_item"><a href="{{prevpage}}">prev</a></span>
{% endif %}
{% if nextpage %}
<span class="paginator_item"><a href="{{nextpage}}">next</a></span>
{% endif %}
</div>
{% endblock %} {% endblock %}

View File

@ -1 +1,24 @@
{% extends "frontend/base.html" %} {% extends "frontend/base.html" %}
{% load chanup %}
{% block content %}
<div class="frontpage posts">
{% for post in posts %}
<div class="post">
<div class="header">
<span class="boardname"><a href="{{post.newsgroup.get_absolute_url}}">{{post.newsgroup.name}}</a></span>
</div>
<div class="header">
<span class="name">{{post.name}}</span> <span class="subject">{{post.subject}}</span>
<span class="posted">{{post.postdate|date}}</span>
<span class="cite"><a href="{{post.get_absolute_url}}">&gt;&gt;{{post.shorthash}}</a></span>
</div>
<div class="attachments">
{% for a in post.attachments.all %}
<a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a>
{% endfor %}
</div>
<pre class="postbody">{{post.message | truncate | memepost }}</pre>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,13 +1,16 @@
{% extends "frontend/base.html" %} {% extends "frontend/base.html" %}
{% load chanup %} {% load chanup %}
{% block title %} {{op.subject}} {% endblock %} {% block title %} {{op.subject}} {% endblock %}
{% block navbar_links %}
<span class="navbar_link"><a href="{{op.newsgroup.get_absolute_url}}">back to {{op.newsgroup.name}}</a></span>
{% endblock %}
{% block content %} {% block content %}
<div id="{{op.posthash}}" class="thread"> <div id="{{op.posthash}}" class="thread">
<div class="post op"> <div class="post op">
<div class="header"> <div class="header">
<span class="name">{{op.name}}</span> <span class="subject">{{op.subject}}</span> <span class="name">{{op.name}}</span> <span class="subject">{{op.subject}}</span>
<span class="msgid">{{op.msgid}}</span> <span class="msgid">{{op.msgid}}</span>
<span class="posted">{{op.posted}}</span> <span class="posted">{{op.postdate|date}}</span>
<span class="cite"><a href="{{op.get_absolute_url}}">&gt;&gt;{{op.shorthash}}</a></span> <span class="cite"><a href="{{op.get_absolute_url}}">&gt;&gt;{{op.shorthash}}</a></span>
</div> </div>
@ -19,11 +22,11 @@
<pre class="postbody">{{op.message|memepost}}</pre> <pre class="postbody">{{op.message|memepost}}</pre>
</div> </div>
{% for reply in op.get_all_replies %} {% for reply in op.get_all_replies %}
<div class="post reply"> <div class="post reply" id="{{reply.posthash}}">
<div class="header"> <div class="header">
<span class="name">{{reply.name}}</span> <span class="subject">{{reply.subject}}</span> <span class="name">{{reply.name}}</span> <span class="subject">{{reply.subject}}</span>
<span class="msgid">{{reply.msgid}}</span> <span class="msgid">{{reply.msgid}}</span>
<span class="posted">{{reply.posted}}</span> <span class="posted">{{reply.postdate|date}}</span>
<span class="cite"><a href="{{reply.get_absolute_url}}">&gt;&gt;{{reply.shorthash}}</a></span> <span class="cite"><a href="{{reply.get_absolute_url}}">&gt;&gt;{{reply.shorthash}}</a></span>
</div> </div>
<div class="attachments"> <div class="attachments">

View File

@ -31,9 +31,10 @@ def webhook(request):
if not util.newsgroup_valid(newsgroup): if not util.newsgroup_valid(newsgroup):
raise Exception("invalid newsgroup name") raise Exception("invalid newsgroup name")
bump = True
group, created = Newsgroup.objects.get_or_create(name=newsgroup) group, created = Newsgroup.objects.get_or_create(name=newsgroup)
if created:
group.save()
if group.banned: if group.banned:
raise Exception("newsgroup is banned") raise Exception("newsgroup is banned")
@ -42,16 +43,24 @@ def webhook(request):
if h in msg: if h in msg:
msgid = msg[h] msgid = msg[h]
break break
# check for sage
if 'X-Sage' in msg and msg['X-Sage'] == '1':
bump = False
if msgid is None: if msgid is None:
raise Exception("no message id specified") raise Exception("no message id specified")
elif not util.msgid_valid(msgid): elif not util.msgid_valid(msgid):
raise Exception("invalid message id format: {}".format(msgid)) raise Exception("invalid message id format: {}".format(msgid))
opmsgid = msgid
h = util.hashid(msgid) h = util.hashid(msgid)
atts = list() atts = list()
ref = msg['References'] or '' ref = msg['References'] or ''
posted = email.utils.parsedate_to_datetime(msg['Date']) posted = util.time_int(email.utils.parsedate_to_datetime(msg['Date']))
if len(ref) > 0:
opmsgid = ref
f = msg['From'] or 'anon <anon@anon>' f = msg['From'] or 'anon <anon@anon>'
name = email.utils.parseaddr(f)[0] name = email.utils.parseaddr(f)[0]
@ -59,6 +68,7 @@ def webhook(request):
'posthash': h, 'posthash': h,
'reference': ref, 'reference': ref,
'posted': posted, 'posted': posted,
'last_bumped': 0,
'name': name, 'name': name,
'subject': msg["Subject"] or '', 'subject': msg["Subject"] or '',
'newsgroup': group}, msgid=msgid) 'newsgroup': group}, msgid=msgid)
@ -96,6 +106,20 @@ def webhook(request):
for att in atts: for att in atts:
post.attachments.add(att) post.attachments.add(att)
op, _ = Post.objects.get_or_create(defaults={
'posthash': util.hashid(opmsgid),
'reference': '',
'posted': 0,
'last_bumped': 0,
'name': 'OP',
'subject': 'OP Not Found',
'newsgroup': group}, msgid=opmsgid)
if bump:
op.bump()
op.save()
except Exception as ex: except Exception as ex:
traceback.print_exc() traceback.print_exc()
return JsonResponse({ 'error': '{}'.format(ex) }) return JsonResponse({ 'error': '{}'.format(ex) })