|
|
import re
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
from django import template
|
|
|
|
|
|
ELLIPSIZER = '...'
|
|
|
|
|
|
REGEX_LINES = re.compile(r'(<div class="br"></div>)', re.U | re.S)
|
|
|
REGEX_TAG = re.compile(r'<(/)?([^ ]+?)(?:(\s*/)| .*?)?>', re.S)
|
|
|
|
|
|
IMG_ACTION_URL = '[<a href="{}">{}</a>]'
|
|
|
|
|
|
|
|
|
register = template.Library()
|
|
|
|
|
|
actions = [
|
|
|
{
|
|
|
'name': 'google',
|
|
|
'link': 'http://google.com/searchbyimage?image_url=%s',
|
|
|
},
|
|
|
{
|
|
|
'name': 'iqdb',
|
|
|
'link': 'http://iqdb.org/?url=%s',
|
|
|
},
|
|
|
]
|
|
|
|
|
|
|
|
|
@register.simple_tag(name='post_url')
|
|
|
def post_url(*args, **kwargs):
|
|
|
post_id = args[0]
|
|
|
|
|
|
post = get_object_or_404('Post', id=post_id)
|
|
|
|
|
|
return post.get_url()
|
|
|
|
|
|
|
|
|
@register.simple_tag(name='image_actions')
|
|
|
def image_actions(*args, **kwargs):
|
|
|
image_link = args[0]
|
|
|
if len(args) > 1:
|
|
|
image_link = 'http://' + args[1] + image_link # TODO https?
|
|
|
|
|
|
return ', '.join([IMG_ACTION_URL.format(
|
|
|
action['link'] % image_link, action['name'])for action in actions])
|
|
|
|
|
|
|
|
|
# TODO Use get_view of a post instead of this
|
|
|
@register.inclusion_tag('boards/post.html', name='post_view')
|
|
|
def post_view(post, moderator=False, need_open_link=False, truncated=False,
|
|
|
**kwargs):
|
|
|
"""
|
|
|
Get post
|
|
|
"""
|
|
|
|
|
|
thread = post.get_thread()
|
|
|
is_opening = post.is_opening()
|
|
|
can_bump = thread.can_bump()
|
|
|
|
|
|
if is_opening:
|
|
|
opening_post_id = post.id
|
|
|
else:
|
|
|
opening_post_id = thread.get_opening_post_id()
|
|
|
|
|
|
return {
|
|
|
'post': post,
|
|
|
'moderator': moderator,
|
|
|
'is_opening': is_opening,
|
|
|
'thread': thread,
|
|
|
'bumpable': can_bump,
|
|
|
'need_open_link': need_open_link,
|
|
|
'truncated': truncated,
|
|
|
'opening_post_id': opening_post_id,
|
|
|
}
|
|
|
|
|
|
|
|
|
# TODO Fix or remove this method
|
|
|
@register.filter(is_safe=True)
|
|
|
def truncate_lines(text, length):
|
|
|
if length <= 0:
|
|
|
return ''
|
|
|
|
|
|
html4_singlets = (
|
|
|
'br', 'col', 'link', 'base', 'img',
|
|
|
'param', 'area', 'hr', 'input'
|
|
|
)
|
|
|
|
|
|
# Count non-HTML chars/words and keep note of open tags
|
|
|
pos = 0
|
|
|
end_text_pos = 0
|
|
|
current_len = 0
|
|
|
open_tags = []
|
|
|
|
|
|
while current_len <= length:
|
|
|
m = REGEX_LINES.search(text, pos)
|
|
|
if not m:
|
|
|
# Checked through whole string
|
|
|
break
|
|
|
pos = m.end(0)
|
|
|
if m.group(1):
|
|
|
# It's an actual non-HTML word or char
|
|
|
current_len += 1
|
|
|
if current_len == length:
|
|
|
end_text_pos = m.start(0)
|
|
|
continue
|
|
|
# Check for tag
|
|
|
tag = REGEX_TAG.match(m.group(0))
|
|
|
if not tag or current_len >= length:
|
|
|
# Don't worry about non tags or tags after our truncate point
|
|
|
continue
|
|
|
closing_tag, tagname, self_closing = tag.groups()
|
|
|
# Element names are always case-insensitive
|
|
|
tagname = tagname.lower()
|
|
|
if self_closing or tagname in html4_singlets:
|
|
|
pass
|
|
|
elif closing_tag:
|
|
|
# Check for match in open tags list
|
|
|
try:
|
|
|
i = open_tags.index(tagname)
|
|
|
except ValueError:
|
|
|
pass
|
|
|
else:
|
|
|
# SGML: An end tag closes, back to the matching start tag,
|
|
|
# all unclosed intervening start tags with omitted end tags
|
|
|
open_tags = open_tags[i + 1:]
|
|
|
else:
|
|
|
# Add it to the start of the open tags list
|
|
|
open_tags.insert(0, tagname)
|
|
|
|
|
|
if current_len <= length:
|
|
|
return text
|
|
|
out = text[:end_text_pos]
|
|
|
|
|
|
if not out.endswith(ELLIPSIZER):
|
|
|
out += ELLIPSIZER
|
|
|
# Close any tags still open
|
|
|
for tag in open_tags:
|
|
|
out += '</%s>' % tag
|
|
|
# Return string
|
|
|
return out
|
|
|
|