##// END OF EJS Templates
Adding z-index to image previews automatically
Adding z-index to image previews automatically

File last commit:

r533:a9b2c757 default
r541:90d66ad5 1.7-dev
Show More
__init__.py
609 lines | 17.1 KiB | text/x-python | PythonLexer
neko259
Deleting old users when the new are created. Only empty users without posts and tags are removed
r486 from datetime import datetime, timedelta
neko259
Moved get_post to an API views module
r491
neko259
Deleting old users when the new are created. Only empty users without posts and tags are removed
r486 from django.db.models import Count
neko259
Moved get_post to an API views module
r491
neko259
Deleting old users when the new are created. Only empty users without posts and tags are removed
r486 OLD_USER_AGE_DAYS = 90
neko259
Moved views to a folder-based model to split it into different files
r443 __author__ = 'neko259'
import hashlib
import string
import time
import re
from django.core import serializers
from django.core.urlresolvers import reverse
neko259
Added metadata to the gallery. Added links to search the image by the online search engines
r460 from django.http import HttpResponseRedirect, Http404
neko259
Moved views to a folder-based model to split it into different files
r443 from django.http.response import HttpResponse
from django.template import RequestContext
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.db import transaction
from django.views.decorators.cache import cache_page
from django.views.i18n import javascript_catalog
neko259
Using django paginator instead of manual pagination
r493 from django.core.paginator import Paginator
neko259
Moved views to a folder-based model to split it into different files
r443
from boards import forms
import boards
from boards import utils
from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
neko259
Using django paginator instead of manual pagination
r493 from boards.models import Post, Tag, Ban, User, Thread
neko259
Moved views to a folder-based model to split it into different files
r443 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
from boards.models.user import RANK_USER
from boards import authors
from boards.utils import get_client_ip
import neboard
BAN_REASON_SPAM = 'Autoban: spam bot'
neko259
Added metadata to the gallery. Added links to search the image by the online search engines
r460 MODE_GALLERY = 'gallery'
MODE_NORMAL = 'normal'
neko259
Moved views to a folder-based model to split it into different files
r443
neko259
Using django paginator instead of manual pagination
r493 DEFAULT_PAGE = 1
neko259
Moved views to a folder-based model to split it into different files
r443
neko259
Using django paginator instead of manual pagination
r493
def index(request, page=DEFAULT_PAGE):
neko259
Moved views to a folder-based model to split it into different files
r443 context = _init_default_context(request)
if utils.need_include_captcha(request):
threadFormClass = ThreadCaptchaForm
kwargs = {'request': request}
else:
threadFormClass = ThreadForm
kwargs = {}
if request.method == 'POST':
form = threadFormClass(request.POST, request.FILES,
error_class=PlainErrorList, **kwargs)
form.session = request.session
if form.is_valid():
return _new_post(request, form)
if form.need_to_ban:
# Ban user because he is suspected to be a bot
_ban_current_user(request)
else:
form = threadFormClass(error_class=PlainErrorList, **kwargs)
threads = []
for thread_to_show in Post.objects.get_threads(page=int(page)):
threads.append(_get_template_thread(thread_to_show))
# TODO Make this generic for tag and threads list pages
context['threads'] = None if len(threads) == 0 else threads
context['form'] = form
neko259
Using django paginator instead of manual pagination
r493 paginator = Paginator(Thread.objects.filter(archived=False),
neboard.settings.THREADS_PER_PAGE)
_get_page_context(paginator, context, page)
neko259
Moved views to a folder-based model to split it into different files
r443
return render(request, 'boards/posting_general.html',
context)
neko259
Using django paginator instead of manual pagination
r493 def archive(request, page=DEFAULT_PAGE):
neko259
Added new popularity counter for tags
r509 """
Get archived posts
"""
neko259
Fixed threads title in the browser title bar. Moving old threads to archive instead of deleting them.
r484 context = _init_default_context(request)
threads = []
for thread_to_show in Post.objects.get_threads(page=int(page),
archived=True):
threads.append(_get_template_thread(thread_to_show))
context['threads'] = threads
neko259
Using django paginator instead of manual pagination
r493 paginator = Paginator(Thread.objects.filter(archived=True),
neboard.settings.THREADS_PER_PAGE)
_get_page_context(paginator, context, page)
neko259
Fixed threads title in the browser title bar. Moving old threads to archive instead of deleting them.
r484
return render(request, 'boards/archive.html', context)
neko259
Moved views to a folder-based model to split it into different files
r443 @transaction.atomic
neko259
Added posting over ajax
r533 def _new_post(request, form, opening_post=None, html_response=True):
neko259
Moved views to a folder-based model to split it into different files
r443 """Add a new post (in thread or as a reply)."""
ip = get_client_ip(request)
is_banned = Ban.objects.filter(ip=ip).exists()
if is_banned:
neko259
Added posting over ajax
r533 if html_response:
return redirect(you_are_banned)
else:
return
neko259
Moved views to a folder-based model to split it into different files
r443
data = form.cleaned_data
title = data['title']
text = data['text']
text = _remove_invalid_links(text)
if 'image' in data.keys():
image = data['image']
else:
image = None
tags = []
if not opening_post:
tag_strings = data['tags']
if tag_strings:
tag_strings = tag_strings.split(' ')
for tag_name in tag_strings:
tag_name = string.lower(tag_name.strip())
if len(tag_name) > 0:
tag, created = Tag.objects.get_or_create(name=tag_name)
tags.append(tag)
post_thread = None
else:
post_thread = opening_post.thread_new
post = Post.objects.create_post(title=title, text=text, ip=ip,
thread=post_thread, image=image,
tags=tags, user=_get_user(request))
thread_to_show = (opening_post.id if opening_post else post.id)
neko259
Added posting over ajax
r533 if html_response:
if opening_post:
return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
'#' + str(post.id))
else:
return redirect(thread, post_id=thread_to_show)
neko259
Moved views to a folder-based model to split it into different files
r443
neko259
Using django paginator instead of manual pagination
r493 def tag(request, tag_name, page=DEFAULT_PAGE):
neko259
Moved views to a folder-based model to split it into different files
r443 """
Get all tag threads. Threads are split in pages, so some page is
neko259
Using django paginator instead of manual pagination
r493 requested.
neko259
Moved views to a folder-based model to split it into different files
r443 """
tag = get_object_or_404(Tag, name=tag_name)
threads = []
for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
threads.append(_get_template_thread(thread_to_show))
if request.method == 'POST':
form = ThreadForm(request.POST, request.FILES,
error_class=PlainErrorList)
form.session = request.session
if form.is_valid():
return _new_post(request, form)
if form.need_to_ban:
# Ban user because he is suspected to be a bot
_ban_current_user(request)
else:
form = forms.ThreadForm(initial={'tags': tag_name},
error_class=PlainErrorList)
context = _init_default_context(request)
context['threads'] = None if len(threads) == 0 else threads
context['tag'] = tag
neko259
Using django paginator instead of manual pagination
r493 paginator = Paginator(Post.objects.get_threads(tag=tag),
neboard.settings.THREADS_PER_PAGE)
_get_page_context(paginator, context, page)
neko259
Moved views to a folder-based model to split it into different files
r443
context['form'] = form
return render(request, 'boards/posting_general.html',
context)
neko259
Added metadata to the gallery. Added links to search the image by the online search engines
r460 def thread(request, post_id, mode=MODE_NORMAL):
neko259
Moved views to a folder-based model to split it into different files
r443 """Get all thread posts"""
if utils.need_include_captcha(request):
postFormClass = PostCaptchaForm
kwargs = {'request': request}
else:
postFormClass = PostForm
kwargs = {}
neko259
Optimized one query, removed debug code from settings so that it is run only...
r472 opening_post = get_object_or_404(Post, id=post_id)
neko259
Check if the post is OP when opening a thread with post ID
r490
# If this is not OP, don't show it as it is
if not opening_post.is_opening():
raise Http404
neko259
Fixed threads title in the browser title bar. Moving old threads to archive instead of deleting them.
r484 if request.method == 'POST' and not opening_post.thread_new.archived:
neko259
Moved views to a folder-based model to split it into different files
r443 form = postFormClass(request.POST, request.FILES,
error_class=PlainErrorList, **kwargs)
form.session = request.session
if form.is_valid():
return _new_post(request, form, opening_post)
if form.need_to_ban:
# Ban user because he is suspected to be a bot
_ban_current_user(request)
else:
form = postFormClass(error_class=PlainErrorList, **kwargs)
neko259
Optimized one query, removed debug code from settings so that it is run only...
r472 thread_to_show = opening_post.thread_new
neko259
Moved views to a folder-based model to split it into different files
r443
context = _init_default_context(request)
posts = thread_to_show.get_replies()
context['form'] = form
context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
context["thread"] = thread_to_show
neko259
Added metadata to the gallery. Added links to search the image by the online search engines
r460 if MODE_NORMAL == mode:
neko259
Speed up gallery loading a bit. Removed duplicate getting of post list in...
r467 context['bumpable'] = thread_to_show.can_bump()
if context['bumpable']:
context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
.count()
context['bumplimit_progress'] = str(
float(context['posts_left']) /
neboard.settings.MAX_POSTS_PER_THREAD * 100)
context['posts'] = posts
neko259
Added a server-side gallery and mode switcher
r458 document = 'boards/thread.html'
neko259
Added metadata to the gallery. Added links to search the image by the online search engines
r460 elif MODE_GALLERY == mode:
neko259
Speed up gallery loading a bit. Removed duplicate getting of post list in...
r467 context['posts'] = posts.filter(image_width__gt=0)
neko259
Added a server-side gallery and mode switcher
r458 document = 'boards/thread_gallery.html'
else:
neko259
Added metadata to the gallery. Added links to search the image by the online search engines
r460 raise Http404
neko259
Added a server-side gallery and mode switcher
r458
return render(request, document, context)
neko259
Moved views to a folder-based model to split it into different files
r443
def login(request):
"""Log in with user id"""
context = _init_default_context(request)
if request.method == 'POST':
form = LoginForm(request.POST, request.FILES,
error_class=PlainErrorList)
form.session = request.session
if form.is_valid():
user = User.objects.get(user_id=form.cleaned_data['user_id'])
request.session['user_id'] = user.id
return redirect(index)
else:
form = LoginForm()
context['form'] = form
return render(request, 'boards/login.html', context)
def settings(request):
"""User's settings"""
context = _init_default_context(request)
user = _get_user(request)
is_moderator = user.is_moderator()
if request.method == 'POST':
with transaction.atomic():
if is_moderator:
form = ModeratorSettingsForm(request.POST,
error_class=PlainErrorList)
else:
form = SettingsForm(request.POST, error_class=PlainErrorList)
if form.is_valid():
selected_theme = form.cleaned_data['theme']
user.save_setting('theme', selected_theme)
if is_moderator:
moderate = form.cleaned_data['moderate']
user.save_setting(SETTING_MODERATE, moderate)
return redirect(settings)
else:
selected_theme = _get_theme(request)
if is_moderator:
form = ModeratorSettingsForm(initial={'theme': selected_theme,
'moderate': context['moderator']},
error_class=PlainErrorList)
else:
form = SettingsForm(initial={'theme': selected_theme},
error_class=PlainErrorList)
context['form'] = form
return render(request, 'boards/settings.html', context)
def all_tags(request):
"""All tags list"""
context = _init_default_context(request)
context['all_tags'] = Tag.objects.get_not_empty_tags()
return render(request, 'boards/tags.html', context)
def jump_to_post(request, post_id):
"""Determine thread in which the requested post is and open it's page"""
post = get_object_or_404(Post, id=post_id)
if not post.thread:
return redirect(thread, post_id=post.id)
else:
return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
+ '#' + str(post.id))
def authors(request):
"""Show authors list"""
context = _init_default_context(request)
context['authors'] = boards.authors.authors
return render(request, 'boards/authors.html', context)
@transaction.atomic
def delete(request, post_id):
"""Delete post"""
user = _get_user(request)
post = get_object_or_404(Post, id=post_id)
if user.is_moderator():
# TODO Show confirmation page before deletion
Post.objects.delete_post(post)
if not post.thread:
return _redirect_to_next(request)
else:
return redirect(thread, post_id=post.thread.id)
@transaction.atomic
def ban(request, post_id):
"""Ban user"""
user = _get_user(request)
post = get_object_or_404(Post, id=post_id)
if user.is_moderator():
# TODO Show confirmation page before ban
ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
if created:
ban.reason = 'Banned for post ' + str(post_id)
ban.save()
return _redirect_to_next(request)
def you_are_banned(request):
"""Show the page that notifies that user is banned"""
context = _init_default_context(request)
ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
context['ban_reason'] = ban.reason
return render(request, 'boards/staticpages/banned.html', context)
def page_404(request):
"""Show page 404 (not found error)"""
context = _init_default_context(request)
return render(request, 'boards/404.html', context)
@transaction.atomic
def tag_subscribe(request, tag_name):
"""Add tag to favorites"""
user = _get_user(request)
tag = get_object_or_404(Tag, name=tag_name)
if not tag in user.fav_tags.all():
user.add_tag(tag)
return _redirect_to_next(request)
@transaction.atomic
def tag_unsubscribe(request, tag_name):
"""Remove tag from favorites"""
user = _get_user(request)
tag = get_object_or_404(Tag, name=tag_name)
if tag in user.fav_tags.all():
user.remove_tag(tag)
return _redirect_to_next(request)
def static_page(request, name):
"""Show a static page that needs only tags list and a CSS"""
context = _init_default_context(request)
return render(request, 'boards/staticpages/' + name + '.html', context)
def api_get_post(request, post_id):
"""
Get the JSON of a post. This can be
used as and API for external clients.
"""
post = get_object_or_404(Post, id=post_id)
json = serializers.serialize("json", [post], fields=(
"pub_time", "_text_rendered", "title", "text", "image",
"image_width", "image_height", "replies", "tags"
))
return HttpResponse(content=json)
@cache_page(86400)
def cached_js_catalog(request, domain='djangojs', packages=None):
return javascript_catalog(request, domain, packages)
def _get_theme(request, user=None):
"""Get user's CSS theme"""
if not user:
user = _get_user(request)
theme = user.get_setting('theme')
if not theme:
theme = neboard.settings.DEFAULT_THEME
return theme
def _init_default_context(request):
"""Create context with default values that are used in most views"""
context = RequestContext(request)
user = _get_user(request)
context['user'] = user
context['tags'] = user.get_sorted_fav_tags()
context['posts_per_day'] = float(Post.objects.get_posts_per_day())
theme = _get_theme(request, user)
context['theme'] = theme
context['theme_css'] = 'css/' + theme + '/base_page.css'
# This shows the moderator panel
moderate = user.get_setting(SETTING_MODERATE)
if moderate == 'True':
context['moderator'] = user.is_moderator()
else:
context['moderator'] = False
return context
def _get_user(request):
"""
Get current user from the session. If the user does not exist, create
a new one.
"""
session = request.session
if not 'user_id' in session:
request.session.save()
md5 = hashlib.md5()
md5.update(session.session_key)
new_id = md5.hexdigest()
neko259
Added some security to prevent generation of the same user ids
r485 while User.objects.filter(user_id=new_id).exists():
md5.update(str(timezone.now()))
new_id = md5.hexdigest()
neko259
Moved views to a folder-based model to split it into different files
r443 time_now = timezone.now()
user = User.objects.create(user_id=new_id, rank=RANK_USER,
registration_time=time_now)
neko259
Deleting old users when the new are created. Only empty users without posts and tags are removed
r486 _delete_old_users()
neko259
Moved views to a folder-based model to split it into different files
r443 session['user_id'] = user.id
else:
user = User.objects.get(id=session['user_id'])
return user
def _redirect_to_next(request):
"""
If a 'next' parameter was specified, redirect to the next page. This is
used when the user is required to return to some page after the current
view has finished its work.
"""
if 'next' in request.GET:
next_page = request.GET['next']
return HttpResponseRedirect(next_page)
else:
return redirect(index)
@transaction.atomic
def _ban_current_user(request):
"""Add current user to the IP ban list"""
ip = utils.get_client_ip(request)
ban, created = Ban.objects.get_or_create(ip=ip)
if created:
ban.can_read = False
ban.reason = BAN_REASON_SPAM
ban.save()
def _remove_invalid_links(text):
"""
Replace invalid links in posts so that they won't be parsed.
Invalid links are links to non-existent posts
"""
for reply_number in re.finditer(REGEX_REPLY, text):
post_id = reply_number.group(1)
post = Post.objects.filter(id=post_id)
if not post.exists():
text = string.replace(text, '>>' + post_id, post_id)
return text
def _datetime_to_epoch(datetime):
return int(time.mktime(timezone.localtime(
datetime,timezone.get_current_timezone()).timetuple())
* 1000000 + datetime.microsecond)
def _get_template_thread(thread_to_show):
"""Get template values for thread"""
last_replies = thread_to_show.get_last_replies()
skipped_replies_count = thread_to_show.get_replies().count() \
- len(last_replies) - 1
return {
'thread': thread_to_show,
'op': thread_to_show.get_replies()[0],
'bumpable': thread_to_show.can_bump(),
'last_replies': last_replies,
'skipped_replies': skipped_replies_count,
}
neko259
Deleting old users when the new are created. Only empty users without posts and tags are removed
r486
def _delete_old_users():
"""
Delete users with no favorite tags and posted messages. These can be spam
bots or just old user accounts
"""
old_registration_date = datetime.now().date() - timedelta(OLD_USER_AGE_DAYS)
for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
tags_count=0).filter(registration_time__lt=old_registration_date):
if not Post.objects.filter(user=user).exists():
user.delete()
neko259
Using django paginator instead of manual pagination
r493
def _get_page_context(paginator, context, page):
"""
Get pagination context variables
"""
context['paginator'] = paginator
context['current_page'] = paginator.page(int(page))