|
|
import json
|
|
|
import logging
|
|
|
|
|
|
from django.core import serializers
|
|
|
from django.db import transaction
|
|
|
from django.db.models import Q
|
|
|
from django.http import HttpResponse, HttpResponseBadRequest
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
from django.views.decorators.csrf import csrf_protect
|
|
|
|
|
|
from boards.abstracts.settingsmanager import get_settings_manager
|
|
|
from boards.forms import PostForm, PlainErrorList
|
|
|
from boards.mdx_neboard import Parser
|
|
|
from boards.models import Post, Thread, Tag, TagAlias
|
|
|
from boards.models.attachment import AttachmentSticker
|
|
|
from boards.models.thread import STATUS_ARCHIVE
|
|
|
from boards.models.user import Notification
|
|
|
from boards.utils import datetime_to_epoch
|
|
|
from boards.views.thread import ThreadView
|
|
|
|
|
|
__author__ = 'neko259'
|
|
|
|
|
|
PARAMETER_TRUNCATED = 'truncated'
|
|
|
PARAMETER_TAG = 'tag'
|
|
|
PARAMETER_OFFSET = 'offset'
|
|
|
PARAMETER_DIFF_TYPE = 'type'
|
|
|
PARAMETER_POST = 'post'
|
|
|
PARAMETER_UPDATED = 'updated'
|
|
|
PARAMETER_LAST_UPDATE = 'last_update'
|
|
|
PARAMETER_THREAD = 'thread'
|
|
|
PARAMETER_UIDS = 'uids'
|
|
|
PARAMETER_SUBSCRIBED = 'subscribed'
|
|
|
|
|
|
DIFF_TYPE_HTML = 'html'
|
|
|
DIFF_TYPE_JSON = 'json'
|
|
|
|
|
|
STATUS_OK = 'ok'
|
|
|
STATUS_ERROR = 'error'
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
@transaction.atomic
|
|
|
def api_get_threaddiff(request):
|
|
|
"""
|
|
|
Gets posts that were changed or added since time
|
|
|
"""
|
|
|
|
|
|
thread_id = request.POST.get(PARAMETER_THREAD)
|
|
|
uids_str = request.POST.get(PARAMETER_UIDS)
|
|
|
|
|
|
if not thread_id or not uids_str:
|
|
|
return HttpResponse(content='Invalid request.')
|
|
|
|
|
|
uids = uids_str.strip().split(' ')
|
|
|
|
|
|
opening_post = get_object_or_404(Post, id=thread_id)
|
|
|
thread = opening_post.get_thread()
|
|
|
|
|
|
json_data = {
|
|
|
PARAMETER_UPDATED: [],
|
|
|
PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
|
|
|
}
|
|
|
posts = Post.objects.filter(thread=thread).exclude(uid__in=uids)
|
|
|
|
|
|
diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
|
|
|
|
|
|
for post in posts:
|
|
|
json_data[PARAMETER_UPDATED].append(post.get_post_data(
|
|
|
format_type=diff_type, request=request))
|
|
|
json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
|
|
|
|
|
|
settings_manager = get_settings_manager(request)
|
|
|
json_data[PARAMETER_SUBSCRIBED] = str(settings_manager.thread_is_fav(opening_post))
|
|
|
|
|
|
# If the tag is favorite, update the counter
|
|
|
settings_manager = get_settings_manager(request)
|
|
|
favorite = settings_manager.thread_is_fav(opening_post)
|
|
|
if favorite:
|
|
|
settings_manager.add_or_read_fav_thread(opening_post)
|
|
|
|
|
|
return HttpResponse(content=json.dumps(json_data))
|
|
|
|
|
|
|
|
|
@csrf_protect
|
|
|
def api_add_post(request, opening_post_id):
|
|
|
"""
|
|
|
Adds a post and return the JSON response for it
|
|
|
"""
|
|
|
|
|
|
opening_post = get_object_or_404(Post, id=opening_post_id)
|
|
|
|
|
|
logger.info('Adding post via api...')
|
|
|
|
|
|
status = STATUS_OK
|
|
|
errors = []
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
|
|
|
form.session = request.session
|
|
|
|
|
|
if form.need_to_ban:
|
|
|
# Ban user because he is suspected to be a bot
|
|
|
# _ban_current_user(request)
|
|
|
status = STATUS_ERROR
|
|
|
if form.is_valid():
|
|
|
post = ThreadView().new_post(request, form, opening_post,
|
|
|
html_response=False)
|
|
|
if not post:
|
|
|
status = STATUS_ERROR
|
|
|
else:
|
|
|
logger.info('Added post #%d via api.' % post.id)
|
|
|
else:
|
|
|
status = STATUS_ERROR
|
|
|
errors = form.as_json_errors()
|
|
|
|
|
|
response = {
|
|
|
'status': status,
|
|
|
'errors': errors,
|
|
|
}
|
|
|
|
|
|
return HttpResponse(content=json.dumps(response))
|
|
|
|
|
|
|
|
|
def get_post(request, post_id):
|
|
|
"""
|
|
|
Gets the html of a post. Used for popups. Post can be truncated if used
|
|
|
in threads list with 'truncated' get parameter.
|
|
|
"""
|
|
|
|
|
|
post = get_object_or_404(Post, id=post_id)
|
|
|
truncated = PARAMETER_TRUNCATED in request.GET
|
|
|
|
|
|
return HttpResponse(content=post.get_view(truncated=truncated, need_op_data=True))
|
|
|
|
|
|
|
|
|
def api_get_threads(request, count):
|
|
|
"""
|
|
|
Gets the JSON thread opening posts list.
|
|
|
Parameters that can be used for filtering:
|
|
|
tag, offset (from which thread to get results)
|
|
|
"""
|
|
|
|
|
|
if PARAMETER_TAG in request.GET:
|
|
|
tag_name = request.GET[PARAMETER_TAG]
|
|
|
if tag_name is not None:
|
|
|
tag = get_object_or_404(Tag, name=tag_name)
|
|
|
threads = tag.get_threads().exclude(status=STATUS_ARCHIVE)
|
|
|
else:
|
|
|
threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
|
|
|
|
|
|
if PARAMETER_OFFSET in request.GET:
|
|
|
offset = request.GET[PARAMETER_OFFSET]
|
|
|
offset = int(offset) if offset is not None else 0
|
|
|
else:
|
|
|
offset = 0
|
|
|
|
|
|
threads = threads.order_by('-bump_time')
|
|
|
threads = threads[offset:offset + int(count)]
|
|
|
|
|
|
opening_posts = []
|
|
|
for thread in threads:
|
|
|
opening_post = thread.get_opening_post()
|
|
|
|
|
|
# TODO Add tags, replies and images count
|
|
|
post_data = opening_post.get_post_data(include_last_update=True)
|
|
|
post_data['status'] = thread.get_status()
|
|
|
|
|
|
opening_posts.append(post_data)
|
|
|
|
|
|
return HttpResponse(content=json.dumps(opening_posts))
|
|
|
|
|
|
|
|
|
# TODO Test this
|
|
|
def api_get_tags(request):
|
|
|
"""
|
|
|
Gets all tags or user tags.
|
|
|
"""
|
|
|
|
|
|
# TODO Get favorite tags for the given user ID
|
|
|
|
|
|
tags = TagAlias.objects.all()
|
|
|
|
|
|
term = request.GET.get('term')
|
|
|
if term is not None:
|
|
|
tags = tags.filter(name__contains=term)
|
|
|
|
|
|
tag_names = [tag.name for tag in tags]
|
|
|
|
|
|
return HttpResponse(content=json.dumps(tag_names))
|
|
|
|
|
|
|
|
|
def api_get_stickers(request):
|
|
|
term = request.GET.get('term')
|
|
|
if not term:
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
global_stickers = AttachmentSticker.objects.filter(Q(name__icontains=term) | Q(stickerpack__name__icontains=term))
|
|
|
local_stickers = [sticker for sticker in get_settings_manager(request).get_stickers() if term in sticker.name]
|
|
|
stickers = list(global_stickers) + local_stickers
|
|
|
|
|
|
image_dict = [{'thumb': sticker.attachment.get_thumb_url(),
|
|
|
'alias': str(sticker)}
|
|
|
for sticker in stickers]
|
|
|
|
|
|
return HttpResponse(content=json.dumps(image_dict))
|
|
|
|
|
|
|
|
|
# TODO The result can be cached by the thread last update time
|
|
|
# TODO Test this
|
|
|
def api_get_thread_posts(request, opening_post_id):
|
|
|
"""
|
|
|
Gets the JSON array of thread posts
|
|
|
"""
|
|
|
|
|
|
opening_post = get_object_or_404(Post, id=opening_post_id)
|
|
|
thread = opening_post.get_thread()
|
|
|
posts = thread.get_replies()
|
|
|
|
|
|
json_data = {
|
|
|
'posts': [],
|
|
|
'last_update': None,
|
|
|
}
|
|
|
json_post_list = []
|
|
|
|
|
|
for post in posts:
|
|
|
json_post_list.append(post.get_post_data())
|
|
|
json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
|
|
|
json_data['posts'] = json_post_list
|
|
|
|
|
|
return HttpResponse(content=json.dumps(json_data))
|
|
|
|
|
|
|
|
|
def api_get_notifications(request, username):
|
|
|
last_notification_id_str = request.GET.get('last', None)
|
|
|
last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
|
|
|
|
|
|
posts = Notification.objects.get_notification_posts(usernames=[username],
|
|
|
last=last_id)
|
|
|
|
|
|
json_post_list = []
|
|
|
for post in posts:
|
|
|
json_post_list.append(post.get_post_data())
|
|
|
return HttpResponse(content=json.dumps(json_post_list))
|
|
|
|
|
|
|
|
|
def api_get_post(request, post_id):
|
|
|
"""
|
|
|
Gets 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)
|
|
|
|
|
|
|
|
|
def api_get_preview(request):
|
|
|
raw_text = request.POST['raw_text']
|
|
|
|
|
|
parser = Parser()
|
|
|
return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
|
|
|
|
|
|
|
|
|
def api_get_new_posts(request):
|
|
|
"""
|
|
|
Gets favorite threads and unread posts count.
|
|
|
"""
|
|
|
posts = list()
|
|
|
|
|
|
include_posts = 'include_posts' in request.GET
|
|
|
|
|
|
settings_manager = get_settings_manager(request)
|
|
|
fav_threads = settings_manager.get_fav_threads()
|
|
|
fav_thread_ops = Post.objects.filter(id__in=fav_threads.keys())\
|
|
|
.order_by('-pub_time').prefetch_related('thread')
|
|
|
|
|
|
ops = [{'op': op, 'last_id': fav_threads[str(op.id)]} for op in fav_thread_ops]
|
|
|
if include_posts:
|
|
|
new_post_threads = Thread.objects.get_new_posts(ops)
|
|
|
if new_post_threads:
|
|
|
thread_ids = {thread.id: thread for thread in new_post_threads}
|
|
|
else:
|
|
|
thread_ids = dict()
|
|
|
|
|
|
for op in fav_thread_ops:
|
|
|
fav_thread_dict = dict()
|
|
|
|
|
|
op_thread = op.get_thread()
|
|
|
if op_thread.id in thread_ids:
|
|
|
thread = thread_ids[op_thread.id]
|
|
|
new_post_count = thread.new_post_count
|
|
|
fav_thread_dict['newest_post_link'] = thread.get_replies()\
|
|
|
.filter(id__gt=fav_threads[str(op.id)])\
|
|
|
.first().get_absolute_url(thread=thread)
|
|
|
else:
|
|
|
new_post_count = 0
|
|
|
fav_thread_dict['new_post_count'] = new_post_count
|
|
|
|
|
|
fav_thread_dict['id'] = op.id
|
|
|
|
|
|
fav_thread_dict['post_url'] = op.get_link_view()
|
|
|
fav_thread_dict['title'] = op.title
|
|
|
|
|
|
posts.append(fav_thread_dict)
|
|
|
else:
|
|
|
fav_thread_dict = dict()
|
|
|
fav_thread_dict['new_post_count'] = \
|
|
|
Thread.objects.get_new_post_count(ops)
|
|
|
posts.append(fav_thread_dict)
|
|
|
|
|
|
return HttpResponse(content=json.dumps(posts))
|
|
|
|