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(
name='Attachment',
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)),
('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)),
('height', models.IntegerField(default=0)),
('banned', models.BooleanField(default=False)),
@ -25,7 +25,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Newsgroup',
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)),
('max_pages', models.IntegerField(default=10)),
('banned', models.BooleanField(default=False)),
@ -34,16 +34,17 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Post',
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)),
('reference', models.CharField(max_length=256, default='')),
('reference', models.CharField(default='', max_length=256)),
('message', models.TextField(default='')),
('subject', models.CharField(max_length=256, default='None')),
('name', models.CharField(max_length=256, default='Anonymous')),
('pubkey', models.CharField(max_length=64, default='')),
('signature', models.CharField(max_length=64, default='')),
('posted', models.DateTimeField()),
('subject', models.CharField(default='None', max_length=256)),
('name', models.CharField(default='Anonymous', max_length=256)),
('pubkey', models.CharField(default='', max_length=64)),
('signature', models.CharField(default='', max_length=64)),
('posted', models.IntegerField(default=0)),
('placeholder', models.BooleanField(default=False)),
('last_bumped', models.IntegerField(default=0)),
('attachments', models.ManyToManyField(to='frontend.Attachment')),
('newsgroup', models.ForeignKey(to='frontend.Newsgroup')),
],

View File

@ -1,8 +1,11 @@
from django.db import models
from django.core.urlresolvers import reverse
from . import util
import mimetypes
from datetime import datetime
class Attachment(models.Model):
"""
@ -35,14 +38,13 @@ class Newsgroup(models.Model):
posts_per_page = models.IntegerField(default=10)
max_pages = models.IntegerField(default=10)
banned = models.BooleanField(default=False)
def get_absolute_url(self):
from django.urls import reverse
return reverse('nntpchan.frontend.views.boardpage', args=[self.name, '0'])
return reverse('board', args=[self.name[9:]])
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)
@ -55,13 +57,14 @@ class Post(models.Model):
signature = models.CharField(max_length=64, default='')
newsgroup = models.ForeignKey(Newsgroup, on_delete=models.CASCADE)
attachments = models.ManyToManyField(Attachment)
posted = models.DateTimeField()
posted = models.IntegerField(default=0)
placeholder = models.BooleanField(default=False)
last_bumped = models.IntegerField(default=0)
def get_all_replies(self):
if self.is_op():
return Post.objects.filter(reference=self.msgid).order_by('posted')
def get_board_replies(self, truncate=5):
rpls = self.get_all_replies()
l = len(rpls)
@ -70,16 +73,23 @@ class Post(models.Model):
return rpls
def is_op(self):
return self.reference == ''
return self.reference == '' or self.reference == self.msgid
def shorthash(self):
return self.posthash[:10]
def postdate(self):
return datetime.fromtimestamp(self.posted)
def get_absolute_url(self):
if self.is_op():
op = util.hashid(self.msgid)
return '/t/{}/'.format(op)
return reverse('thread', args=[op])
else:
op = util.hashid(self.reference)
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 {
background-color: #eef2ff;
background: #EEF2FF url('/static/bg.png') repeat-x 50% 0%;
}
div {
@ -68,6 +68,27 @@ img.thumb {
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 {
float: right;
}

View File

@ -142,4 +142,11 @@ def memepost(text, autoescape=True):
return doFilter(funcs[1:], t, filter)
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 re
import time
def hashid(msgid):
h = hashlib.sha1()
m = '{}'.format(msgid).encode('ascii')
@ -19,8 +21,6 @@ def hashfile(data):
def msgid_valid(msgid):
return re.match("<[a-zA-Z0-9\$\._\-\|]+@[a-zA-Z0-9\$\._\-\|]+>$", msgid) is not None
def save_part(part):
"""
save a mime part to disk
"""
def time_int(dtime):
return time.mktime(dtime.timetuple())

View File

@ -19,8 +19,26 @@ class BoardView(generic.View):
else:
begin = page * group.posts_per_page
end = begin + group.posts_per_page
posts = self.model.objects.filter(newsgroup=group, reference='').order_by('-posted')[begin:end]
return render(request, self.template_name, {'threads': posts, 'page': page, 'name': newsgroup})
print(begin, end)
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):
@ -33,12 +51,16 @@ class ThreadView(generic.ListView):
class FrontPageView(generic.ListView):
class FrontPageView(generic.View):
template_name = 'frontend/frontpage.html'
model = Post
def get_queryset(self):
return self.model.objects.order_by('posted')[:10]
def get(self, request, truncate=5):
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):

View File

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

View File

@ -10,7 +10,8 @@
<body>
<div id="wrapper">
<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_left">{% block navbar_left %}| <a href="#" onclick="nntpchan_toggle_settings()">settings</a>{% endblock %}</span>
</div>
@ -19,9 +20,13 @@
{% endblock %}
</div>
</div>
<footer>
<p> legal notice goes here </p>
</footer>
<hr/>
<center>
<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>
ready();
</script>

View File

@ -2,6 +2,15 @@
{% extends "frontend/base.html" %}
{% load chanup %}
{% 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 %}
<hr />
{% for op in threads %}
@ -9,8 +18,7 @@
<div class="post op">
<div class="header">
<span class="name">{{op.name}}</span> <span class="subject">{{op.subject}}</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>
</div>
@ -19,14 +27,13 @@
<a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a>
{% endfor %}
</div>
<pre class="postbody">{{op.message|memepost}}</pre>
<pre class="postbody">{{op.message|truncate|memepost}}</pre>
</div>
{% for reply in op.get_board_replies %}
<div class="post reply">
<div class="post reply" id="{{reply.posthash}}">
<div class="header">
<span class="name">{{reply.name}}</span> <span class="subject">{{reply.subject}}</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>
</div>
<div class="attachments">
@ -34,10 +41,18 @@
<a href="{{a.source}}"><img class="thumb" src="{{a.thumb}}"></img></a>
{% endfor %}
</div>
<pre class="postbody">{{reply.message|memepost}}</pre>
<pre class="postbody">{{reply.message|truncate|memepost}}</pre>
</div>
{% endfor %}
</div>
<hr />
{% 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 %}

View File

@ -1 +1,24 @@
{% 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" %}
{% load chanup %}
{% 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 %}
<div id="{{op.posthash}}" class="thread">
<div class="post op">
<div class="header">
<span class="name">{{op.name}}</span> <span class="subject">{{op.subject}}</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>
</div>
@ -19,11 +22,11 @@
<pre class="postbody">{{op.message|memepost}}</pre>
</div>
{% for reply in op.get_all_replies %}
<div class="post reply">
<div class="post reply" id="{{reply.posthash}}">
<div class="header">
<span class="name">{{reply.name}}</span> <span class="subject">{{reply.subject}}</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>
</div>
<div class="attachments">

View File

@ -31,9 +31,10 @@ def webhook(request):
if not util.newsgroup_valid(newsgroup):
raise Exception("invalid newsgroup name")
bump = True
group, created = Newsgroup.objects.get_or_create(name=newsgroup)
if created:
group.save()
if group.banned:
raise Exception("newsgroup is banned")
@ -42,23 +43,32 @@ def webhook(request):
if h in msg:
msgid = msg[h]
break
# check for sage
if 'X-Sage' in msg and msg['X-Sage'] == '1':
bump = False
if msgid is None:
raise Exception("no message id specified")
elif not util.msgid_valid(msgid):
raise Exception("invalid message id format: {}".format(msgid))
opmsgid = msgid
h = util.hashid(msgid)
atts = list()
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>'
name = email.utils.parseaddr(f)[0]
post, created = Post.objects.get_or_create(defaults={
'posthash': h,
'reference': ref,
'posted': posted,
'last_bumped': 0,
'name': name,
'subject': msg["Subject"] or '',
'newsgroup': group}, msgid=msgid)
@ -96,6 +106,20 @@ def webhook(request):
for att in atts:
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:
traceback.print_exc()
return JsonResponse({ 'error': '{}'.format(ex) })