model, controller and template tweaks for pastes, add another test

This commit is contained in:
Dan Colish 2011-04-03 16:06:42 -07:00
parent 58115688cd
commit 6ac6777a58
6 changed files with 54 additions and 52 deletions

1
TODO
View File

@ -4,3 +4,4 @@
* Improve i18n support * Improve i18n support
* Improve LodgeIt Interface (null-interface + star) * Improve LodgeIt Interface (null-interface + star)
* use udiff module instead of pygments-diff-highlighter for diff highlighning * use udiff module instead of pygments-diff-highlighter for diff highlighning
* use flatland for forms

View File

@ -15,7 +15,7 @@ from lodgeit.lib import antispam
from lodgeit.i18n import list_languages as i18n_list_languages, _ from lodgeit.i18n import list_languages as i18n_list_languages, _
from lodgeit.utils import render_to_response, url_for from lodgeit.utils import render_to_response, url_for
from lodgeit.models import Paste from lodgeit.models import Paste
from lodgeit.database import db from lodgeit.database import session
from lodgeit.lib.highlighting import list_languages, STYLES, get_style from lodgeit.lib.highlighting import list_languages, STYLES, get_style
from lodgeit.lib.pagination import generate_pagination from lodgeit.lib.pagination import generate_pagination
from lodgeit.lib.captcha import check_hashed_solution, Captcha from lodgeit.lib.captcha import check_hashed_solution, Captcha
@ -24,6 +24,7 @@ from lodgeit.lib.captcha import check_hashed_solution, Captcha
class PasteController(object): class PasteController(object):
"""Provides all the handler callback for paste related stuff.""" """Provides all the handler callback for paste related stuff."""
#XXX:dc: using language here clashes with internationalization terms
def new_paste(self, language=None): def new_paste(self, language=None):
"""The 'create a new paste' view.""" """The 'create a new paste' view."""
language = local.request.args.get('language', language) language = local.request.args.get('language', language)
@ -32,19 +33,16 @@ class PasteController(object):
code = error = '' code = error = ''
show_captcha = private = False show_captcha = private = False
parent = None parent_id = None
req = local.request req = local.request
getform = req.form.get getform = req.form.get
if local.request.method == 'POST': if local.request.method == 'POST':
code = getform('code', u'') code = getform('code', u'')
language = getform('language') language = getform('language')
parent_id = getform('parent') parent_id = getform('parent')
if parent_id is not None:
parent = Paste.get(parent_id)
spam = getform('webpage') or antispam.is_spam(code) spam = getform('webpage') or antispam.is_spam(code)
if spam: if spam:
error = _('your paste contains spam') error = _('your paste contains spam')
captcha = getform('captcha') captcha = getform('captcha')
@ -55,11 +53,12 @@ class PasteController(object):
error = _('your paste contains spam and the ' error = _('your paste contains spam and the '
'CAPTCHA solution was incorrect') 'CAPTCHA solution was incorrect')
show_captcha = True show_captcha = True
if code and language and not error: if code and language and not error:
paste = Paste(code, language, parent, req.user_hash, paste = Paste(code, language, parent_id, req.user_hash,
'private' in req.form) 'private' in req.form)
db.session.add(paste) session.add(paste)
db.session.commit() session.commit()
local.request.session['language'] = language local.request.session['language'] = language
if paste.private: if paste.private:
identifier = paste.private_id identifier = paste.private_id
@ -78,7 +77,7 @@ class PasteController(object):
private = parent.private private = parent.private
return render_to_response('new_paste.html', return render_to_response('new_paste.html',
languages=list_languages(), languages=list_languages(),
parent=parent, parent=parent_id,
code=code, code=code,
language=language, language=language,
error=error, error=error,
@ -165,7 +164,7 @@ class PasteController(object):
old = Paste.get(old_id) old = Paste.get(old_id)
new = Paste.get(new_id) new = Paste.get(new_id)
if old is None or new is None: if not (old or new):
raise NotFound() raise NotFound()
return Response(old.compare_to(new), mimetype='text/plain') return Response(old.compare_to(new), mimetype='text/plain')
@ -175,7 +174,9 @@ class PasteController(object):
back to the page the user is coming from. back to the page the user is coming from.
""" """
style_name = local.request.form.get('style') style_name = local.request.form.get('style')
resp = redirect(local.request.environ.get('HTTP_REFERER') or '/') resp = redirect(local.request.headers.get('referer') or
url_for('pastes/new_paste'))
#XXX:dc: use some sort of form element validation instead
if style_name in STYLES: if style_name in STYLES:
resp.set_cookie('style', style_name) resp.set_cookie('style', style_name)
return resp return resp

View File

@ -37,16 +37,15 @@ class Paste(db.Model):
primaryjoin=parent_id == paste_id, primaryjoin=parent_id == paste_id,
backref=db.backref('parent', remote_side=[paste_id])) backref=db.backref('parent', remote_side=[paste_id]))
def __init__(self, code, language, parent=None, user_hash=None, def __init__(self, code, language, parent_id=None, user_hash=None,
private=False): private=False):
if language not in LANGUAGES: if language not in LANGUAGES:
language = 'text' language = 'text'
self.code = u'\n'.join(code.splitlines()) self.code = u'\n'.join(code.splitlines())
self.language = language self.language = language
if isinstance(parent, Paste): #XXX:dc: set these a bit more sanely, allowing two types is bad
self.parent = parent if parent_id:
elif parent is not None: self.parent_id = parent_id
self.parent_id = parent
self.pub_date = datetime.now() self.pub_date = datetime.now()
self.handled = False self.handled = False
self.user_hash = user_hash self.user_hash = user_hash
@ -58,23 +57,23 @@ class Paste(db.Model):
with their unique hash and public with the paste id. with their unique hash and public with the paste id.
""" """
if isinstance(identifier, basestring) and not identifier.isdigit(): if isinstance(identifier, basestring) and not identifier.isdigit():
return Paste.query.filter(Paste.private_id == identifier).first() query = Paste.query.filter_by(private_id=identifier)
return Paste.query.filter(db.and_( else:
Paste.paste_id == int(identifier), query = Paste.query.filter_by(paste_id=int(identifier))
Paste.private_id == None)).first() return query.first()
@staticmethod @staticmethod
def find_all(): def find_all():
"""Return a query for all public pastes ordered by the id in reverse """Return a query for all public pastes ordered by the id in reverse
order. order.
""" """
return Paste.query.filter(Paste.private_id == None) \ return Paste.query.filter_by(
.order_by(Paste.paste_id.desc()) private_id=None).order_by(Paste.paste_id.desc())
@staticmethod @staticmethod
def count(): def count():
"""Count all pastes.""" """Count all pastes."""
return Paste.query(Paste.paste_id).count() return Paste.query.count()
@staticmethod @staticmethod
def resolve_root(identifier): def resolve_root(identifier):
@ -91,18 +90,17 @@ class Paste(db.Model):
"""Get the new replies for the ower of a request and flag them """Get the new replies for the ower of a request and flag them
as handled. as handled.
""" """
ids = db.session.query(Paste.paste_id) \ ids = [x.paste_id for x in Paste.query.filter_by(
.filter(Paste.user_hash == local.request.user_hash) user_hash=local.request.user_hash).all()]
paste_list = Paste.query.filter(db.and_(
paste_list = db.session.query(Paste.paste_id).filter(db.and_(
Paste.parent_id.in_(ids), Paste.parent_id.in_(ids),
Paste.handled == False, Paste.handled == False,
Paste.user_hash != local.request.user_hash, Paste.user_hash != local.request.user_hash,
)).order_by(Paste.paste_id.desc()).all() )).order_by(Paste.paste_id.desc()).all()
to_mark = [p.paste_id for p in paste_list] to_mark = [p.paste_id for p in paste_list]
Paste.query.filter(Paste.paste_id.in_(to_mark)) \ Paste.query.filter(Paste.paste_id.in_(to_mark)
.update(values={'handled': True}) ).update(values={'handled': True})
db.session.commit() db.session.commit()
return paste_list return paste_list
@ -112,14 +110,9 @@ class Paste(db.Model):
def _set_private(self, value): def _set_private(self, value):
if not value: if not value:
self.private_id = None self.private_id = None
return elif self.private_id is None:
if self.private_id is None: self.private_id = generate_paste_hash()
while 1:
self.private_id = generate_paste_hash()
paste = Paste.query.filter(Paste.private_id ==
self.private_id).first()
if paste is None:
break
private = property(_get_private, _set_private, doc=''' private = property(_get_private, _set_private, doc='''
The private status of the paste. If the paste is private it gets The private status of the paste. If the paste is private it gets
a unique hash as identifier, otherwise an integer. a unique hash as identifier, otherwise an integer.

View File

@ -55,7 +55,7 @@ def generate_paste_hash():
"""Generates a more or less unique-truncated SHA1 hash.""" """Generates a more or less unique-truncated SHA1 hash."""
while 1: while 1:
digest = sha1('%s|%s' % (random(), time.time())).digest() digest = sha1('%s|%s' % (random(), time.time())).digest()
val = _word_only(digest.encode('base64').strip().splitlines()[0])[:20] val = _word_only(digest.encode('base64').strip())[:20]
# sanity check. number only not allowed (though unlikely) # sanity check. number only not allowed (though unlikely)
if not val.isdigit(): if not val.isdigit():
return val return val

View File

@ -1,10 +1,10 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"> "http://www.w3.org/TR/html4/loose.dtd">
<html> <html>
<head> <head>
<title>{{ page_title|e }} | LodgeIt!</title> <title>{{ page_title|e }} | LodgeIt!</title>
<link rel="stylesheet" href="/static/style.css" type="text/css"> <link rel="stylesheet" href="/static/style.css" type="text/css" />
<link rel="stylesheet" href="/static/print.css" type="text/css" media="print"> <link rel="stylesheet" href="/static/print.css" type="text/css" media="print" />
<script type="text/javascript" src="/static/jquery.js"></script> <script type="text/javascript" src="/static/jquery.js"></script>
<script type="text/javascript" src="/static/jquery.autocomplete.js"></script> <script type="text/javascript" src="/static/jquery.autocomplete.js"></script>
<script type="text/javascript" src="/static/cookie.js"></script> <script type="text/javascript" src="/static/cookie.js"></script>
@ -17,23 +17,27 @@
</head> </head>
<body> <body>
<div class="page"> <div class="page">
<div id="header"><h1>Lodge It</h1></div> <div id="header">
<h1>Lodge It</h1>
</div>
<ul id="navigation"> <ul id="navigation">
{%- for href, id, caption in [ {%- for href, id, caption in [
('pastes/new_paste', 'new', _('New')), ('pastes/new_paste', 'new', _('New')),
('pastes/show_all', 'all', _('All')), ('pastes/show_all', 'all', _('All')),
('static/about', 'about', _('About')), ('static/about', 'about', _('About')),
('static/help', 'help', '?') ('static/help', 'help', '?')
] %} ] %}
<li{% if active_page == id %} class="active"{% endif %}> <li {% if active_page == id %} class="active"{% endif %} >
<a href="{{ url_for(href) | e }}">{{ caption|e }}</a> <a href="{{ url_for(href) | e }}">{{ caption|e }}</a>
</li> </li>
{%- endfor %} {%- endfor %}
</ul> </ul>
{# <ul id="languages"> {# <ul id="languages">
{% for lang, name in i18n_languages %} {% for lang, name in i18n_languages %}
<li{% if request.locale.language == lang %} class="active"{% endif %}> <li {% if request.locale.language == lang %}class="active"{% endif %}>
<a href="{{ url_for('pastes/new_paste', language='%s') | format(lang) }}"><img alt="{{ lang }}" src="{{ '/static/flags/%s.png'|format(lang) }}"></a> <a href="{{ url_for('pastes/new_paste', language='%s' % lang) }}">
<img alt="{{ lang }}" src="{{ '/static/flags/%s.png' % lang }}" />
</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> #} </ul> #}
@ -47,9 +51,9 @@
paste=paste.paste_id, paste_url=paste.url|e, parent_url=paste.parent.url|e %} paste=paste.paste_id, paste_url=paste.url|e, parent_url=paste.parent.url|e %}
on {{ date }} someone replied to your paste on {{ date }} someone replied to your paste
<a href="{{ parent_url }}">#{{ parent }}</a>, <a href="{{ parent_url }}">#{{ parent }}</a>,
in paste <a href="{{ paste_url }}">#{{ paste }}</a>. Click here to in paste <a href="{{ paste_url }}">#{{ paste }}</a>. Click here to {% endtrans %}
<a href="/compare/{{ paste }}/{{ parent }}/">compare <a href="{{ url_for('pastes/compare_paste', new_id=paste, old_id=parent) }}">
those two pastes</a>.{% endtrans %} {%- trans %}compare those two pastes{% endtrans %}</a>.
</p> </p>
{% endfor %} {% endfor %}
<p><a href="javascript:LodgeIt.hideNotification()">{% trans %}hide this notification{% endtrans %}</a></p> <p><a href="javascript:LodgeIt.hideNotification()">{% trans %}hide this notification{% endtrans %}</a></p>

View File

@ -8,9 +8,12 @@
We've recently updated this pastebin. While it is out goal for nothing to get We've recently updated this pastebin. While it is out goal for nothing to get
lost, you may have found a page that was mis-placed. Check your URL to ensure lost, you may have found a page that was mis-placed. Check your URL to ensure
you have gone where you intended. If everything looks OK and you still see you have gone where you intended. If everything looks OK and you still see
this error page, please consider {% endtrans %}<a href="{{ url_for('static/about') }}">{% trans %} contacting us{% endtrans %}</a>. this error page, please consider {% endtrans -%}
<a href="{{ url_for('static/about') }}">{% trans %}contacting us{% endtrans %}</a>.
</p> </p>
<p> <p>
{% trans %}Click{% endtrans %} <a href="{{ url_for('pastes/new_paste') }}">{% trans %}here{% endtrans %}</a>{% trans %} to add a new paste.{% endtrans %} {% trans %}Click {% endtrans -%}
<a href="{{ url_for('pastes/new_paste') }}">{% trans %}here{% endtrans %}</a>
{%- trans %} to add a new paste.{% endtrans %}
</p> </p>
{% endblock %} {% endblock %}