- Merge with closedverse-video-support
- user_tools moved to forms.py
- community_tools moved to forms.py
This commit is contained in:
some weird guy
2023-08-19 17:17:00 -07:00
parent 49477bf178
commit 1b33a2aad1
50 changed files with 660 additions and 439 deletions
+144 -85
View File
@@ -2,12 +2,14 @@ from django.http import HttpResponse, HttpResponseNotFound, HttpResponseBadReque
from django.template import loader
from django.shortcuts import render, redirect, get_object_or_404
from django.http import Http404
from django.views.decorators.csrf import csrf_exempt
#from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.core.validators import EmailValidator
from django.core.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password
from django.contrib.auth import update_session_auth_hash
from django.db.models import Q, Count, Exists, OuterRef
from django.db.models.functions import Now
from .models import *
@@ -16,16 +18,12 @@ from closedverse import settings
import re
from django.urls import reverse
from random import getrandbits
from random import choice
from json import dumps, loads
import sys, traceback
import base64
import json
import traceback
import subprocess
from datetime import datetime, timedelta
from django.utils import timezone
import django.utils.dateformat
from binascii import hexlify
from os import urandom
from django.contrib.auth.hashers import identify_hasher
#from silk.profiling.profiler import silk_profile
@@ -67,8 +65,6 @@ def community_list(request):
ad = Ads.get_one()
else:
ad = "no ads"
WelcomeMSG = welcomemsg.objects.filter(show=True).order_by('-order', '-id')
# announcements within the past week-ish
announcements = Post.objects.filter(community__tags='announcements', created__gte=Now()-timedelta(days=5)).order_by('-created')[:6]
if request.user.is_authenticated:
@@ -79,7 +75,6 @@ def community_list(request):
'title': 'Communities',
'ad': ad,
'announcements': announcements,
'WelcomeMSG': WelcomeMSG,
'availableads': availableads,
'classes': classes,
'general': obj.filter(type=0).order_by('-created')[0:12],
@@ -97,7 +92,6 @@ def community_list(request):
'image': request.build_absolute_uri(settings.STATIC_URL + 'img/favicon.png'),
},
})
def community_all(request, category):
"""All communities, with pagination"""
try:
@@ -191,8 +185,9 @@ def community_favorites(request):
def login_page(request):
"""Login page! using our own user objects."""
# Redirect the user to / if they're logged in, forcing them to log out
location = '/'
if request.user.is_authenticated:
return redirect('/')
return redirect(location)
if request.method == 'POST':
# If we don't have all of the POST parameters we want..
if not (request.POST['username'] and request.POST['password']):
@@ -214,7 +209,18 @@ def login_page(request):
successful = False if user[1] is False or not user[0].is_active() else True
LoginAttempt.objects.create(user=user[0], success=successful, user_agent=request.META.get('HTTP_USER_AGENT'), addr=request.META.get('REMOTE_ADDR'))
if user[1] == False:
return HttpResponse("Invalid password.", status=401)
# check if a hasher could not be identified due to moving from passlib
try:
# will throw a ValueError if no hasher is found
identify_hasher(user[0].password)
except ValueError as e:
# error says "Unknown password hashing algorithm ''......", meaning that the password is not converted
if '\'\'' in str(e):
return HttpResponseBadRequest("The password is either encoded in an unsupported format, or the passwords haven't been updated yet. As part of a recent update, passwords need to be converted - sorry about that. If password resets work, you can use that to make your account usable immediately.")
else:
return HttpResponseBadRequest("The password is either encoded in an unsupported format, or the settings haven't been updated yet. Please add - or direct the server owner to add - hashers_passlib.bcrypt_sha256 to settings.PASSWORD_HASHERS, and then install django-hashers-passlib. I'm sorry for the inconvenience! If password resets work, you can use that immediately.")
else:
return HttpResponse("Invalid password.", status=401)
elif user[1] == 2:
return HttpResponse("This account's password needs to be reset. Contact an admin or reset by email.", status=400)
elif not user[0].is_active():
@@ -227,14 +233,17 @@ def login_page(request):
#if request.META['HTTP_REFERER'] and "login" not in request.META['HTTP_REFERER'] and request.META['HTTP_HOST'] in request.META['HTTP_REFERER']:
# location = request.META['HTTP_REFERER']
#else:
location = '/'
if request.GET.get('next'):
location = request.GET['next']
return HttpResponse(location)
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
# because if you respond to an ajax with a redirect, the response will just be the page, you can't get the redirect value from js
return HttpResponse(location)
return redirect(location)
else:
return render(request, 'closedverse_main/login_page.html', {
'title': 'Log in',
'allow_signups': settings.allow_signups,
'reset_supported': hasattr(settings, 'DEFAULT_FROM_EMAIL'),
#'classes': ['no-login-btn']
})
def signup_page(request):
@@ -311,8 +320,14 @@ def signup_page(request):
if not request.POST['password'] == request.POST['password_again']:
return HttpResponseBadRequest("Your passwords don't match.")
# do the length check
if len(request.POST['password']) < settings.minimum_password_length:
return HttpResponseBadRequest('The password must be at least ' + str(settings.minimum_password_length) + ' characters long.')
#if len(request.POST['password']) < settings.minimum_password_length:
# return HttpResponseBadRequest('The password must be at least ' + str(settings.minimum_password_length) + ' characters long.')
# use native django password validators, which can include length check
try:
# todo if you include the user object here it would help validate against some user attributes however this form is not the one that actually makes the account sooo not really doable unless a dummy user object is created for the sole purpose of this check which would be dumb
validate_password(request.POST['password'])
except ValidationError as error:
return HttpResponseBadRequest(error)
if not request.POST['nickname']:
return HttpResponseBadRequest("You need a nickname. What else are we gonna call you????? Ghosty?")
if request.POST['nickname'] and len(request.POST['nickname']) > 32:
@@ -363,7 +378,9 @@ def signup_page(request):
LoginAttempt.objects.create(user=make, success=True, user_agent=request.META.get('HTTP_USER_AGENT'), addr=request.META.get('REMOTE_ADDR'))
login(request, make)
request.session['passwd'] = make.password
return HttpResponse("/")
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
return HttpResponse('/')
return redirect('/')
else:
if not settings.RECAPTCHA_PUBLIC_KEY:
settings.RECAPTCHA_PUBLIC_KEY = None
@@ -383,11 +400,19 @@ def forgot_passwd(request):
try:
user = User.objects.get(email=request.POST['email'])
except (User.DoesNotExist, ValueError):
<<<<<<< Updated upstream
return HttpResponseNotFound("There isn't a user with that email address.")
try:
user.password_reset_email(request)
except:
return HttpResponseBadRequest("There was an error submitting that.")
=======
return HttpResponseNotFound("The email address could not be found.")
try:
user.password_reset_email(request)
except Exception as error:
return HttpResponseBadRequest("There was an error submitting that, sorry! Here's why: " + str(error))
>>>>>>> Stashed changes
return HttpResponse("Success! Check your emails, it should have been sent from \"{0}\".".format(settings.DEFAULT_FROM_EMAIL))
if request.GET.get('token'):
user = User.get_from_passwd(request.GET['token'])
@@ -396,6 +421,10 @@ def forgot_passwd(request):
if request.method == 'POST':
if not request.POST['password'] == request.POST['password_again']:
return HttpResponseBadRequest("Your passwords don't match.")
try:
validate_password(new, user=user)
except ValidationError as error:
return HttpResponseBadRequest(error)
user.set_password(request.POST['password'])
user.save()
return HttpResponse("Success! Now you can log in with your new password!")
@@ -406,13 +435,18 @@ def forgot_passwd(request):
})
return render(request, 'closedverse_main/forgot_page.html', {
'title': 'Reset password',
'reset_supported': hasattr(settings, 'DEFAULT_FROM_EMAIL'),
#'classes': ['no-login-btn'],
})
def logout_page(request):
"""Password email page / post endpoint."""
if not request.user.is_active():
r = HttpResponseForbidden("You can't log out while you're inactive. According to me and God, you'll just have to sit here and suffer for now. Go contemplate your actions. You will be redirected to Wario Land 4 momentarily.", content_type='text/plain')
if not request.user.is_authenticated or not request.user.is_active():
if not request.user.is_authenticated:
logout(request)
r = HttpResponseForbidden("You are not logged in, so how can you possibly log out? You will be redirected to Wario Land 4 momentarily.", content_type='text/plain')
else:
r = HttpResponseForbidden("You can't log out while you're inactive. According to me and God, you'll just have to sit here and suffer for now. Go contemplate your actions. You will be redirected to Wario Land 4 momentarily.", content_type='text/plain')
r['Refresh'] = '7; url=https://gba.js.org/player#warioland4'
return r
logout(request)
@@ -524,7 +558,7 @@ def user_view(request, username):
user.avatar = request.POST.get('mh')
#profile.origin_id = getmii[2]
profile.origin_id = request.POST['origin_id']
profile.origin_info = dumps([request.POST.get('mh'), 'if you see this then something is wrong', request.POST['origin_id']])
profile.origin_info = json.dumps([request.POST.get('mh'), 'if you see this then something is wrong', request.POST['origin_id']])
# set the username color
if request.POST.get('color'):
try:
@@ -1031,6 +1065,7 @@ def community_tools_set(request, community):
can_edit = the_community.can_edit_community(request)
if not can_edit:
return HttpResponseForbidden()
<<<<<<< Updated upstream
if len(request.POST.get('community_name')) == 0 or len(request.POST.get('community_name')) >= 100:
return json_response('Your community name is either too short or too long.')
if len(request.POST.get('community_description')) >= 1024:
@@ -1087,6 +1122,24 @@ def community_tools_set(request, community):
'''
the_community.save()
#AuditLog.objects.create(type=2, user=user, by=request.user, reasoning=request.POST)
=======
form = CommunitySettingForm(request.POST, request.FILES, request , instance=the_community)
if not form.is_valid():
return json_response(form.errors.as_text())
community = form.save(commit=False)
community.name = form.cleaned_data.get('community_name')
community.description = form.cleaned_data.get('community_description')
community.platform = form.cleaned_data.get('community_platform')
community.require_auth = True if form.cleaned_data.get('force_login') else False
if form.cleaned_data.get('community_icon'):
upload = util.image_upload(form.cleaned_data.get('community_icon'), True, icon=True)
community.ico = upload
if form.cleaned_data.get('community_banner'):
upload = util.image_upload(form.cleaned_data.get('community_banner'), True, banner=True)
community.banner = upload
community.save()
AuditLog.objects.create(type=4, community=community, user=community.creator, by=request.user)
>>>>>>> Stashed changes
return HttpResponse()
else:
raise Http404()
@@ -1099,8 +1152,6 @@ def community_create(request):
raise Http404()
return render(request, 'closedverse_main/community_create.html', {
'title': 'Create a community',
'max_icon_size': settings.max_icon_size,
'max_banner_size': settings.max_banner_size,
'tokens': request.user.c_tokens,
})
def community_create_action(request):
@@ -1126,11 +1177,8 @@ def post_create(request, community):
if request.method == 'POST':
# Wake
request.user.wake(request.META['REMOTE_ADDR'])
# Required
if not (request.POST.get('community')):
return HttpResponseBadRequest()
try:
community = Community.objects.get(id=community, unique_id=request.POST['community'])
community = Community.objects.get(id=community)
except (Community.DoesNotExist, ValueError):
return HttpResponseNotFound()
# Method of Community
@@ -1352,13 +1400,13 @@ def comment_delete_yeah(request, comment):
@require_http_methods(['POST'])
@login_required
def poll_vote(request, poll):
the_poll = get_object_or_404(Poll, unique_id=poll)
the_poll = get_object_or_404(Poll, id=poll)
the_poll.vote(request.user, request.POST.get('a'))
return HttpResponse()
@require_http_methods(['POST'])
@login_required
def poll_unvote(request, poll):
the_poll = get_object_or_404(Poll, unique_id=poll)
the_poll = get_object_or_404(Poll, id=poll)
the_poll.unvote(request.user)
return HttpResponse()
@@ -1433,6 +1481,18 @@ def user_addblock(request, username):
user = get_object_or_404(User, username=username)
user.make_block(request.user)
return HttpResponse()
@require_http_methods(['POST'])
@login_required
def user_rmblock(request, username):
user = get_object_or_404(User, username=username)
user.remove_block(request.user)
return HttpResponse()
@login_required
def user_blocklist(request):
blocks = UserBlock.objects.filter(source=request.user).order_by('-created')[:50]
return render(request, 'closedverse_main/block-list.html', {
'blocks': blocks,
})
# Notifications work differently since the Openverse rebranding. (that we changed back)
# They used to respond with a JSON for values for unread notifications and messages.
@@ -1656,7 +1716,10 @@ def messages_read(request, username):
@require_http_methods(['POST'])
@login_required
def message_rm(request, message):
message = get_object_or_404(Message, unique_id=message)
message = get_object_or_404(Message, id=message)
# check that if you aren't the conversation source or target (so if you aren't inside the conversation)
if message.conversation.source != request.user and message.conversation.target != request.user:
raise Http404()
message.rm(request)
return HttpResponse()
@@ -1683,12 +1746,15 @@ def user_tools(request, username):
if not request.user.is_authenticated:
raise Http404()
if not request.user.can_manage():
raise Http404()
return HttpResponseForbidden()
user = get_object_or_404(User, username__iexact=username)
profile = user.profile()
# check if the requesting user is allowed to change someone
if user.has_authority(request.user):
raise Http404()
return HttpResponseForbidden()
user_form = UserForm(instance=user)
profile_form = ProfileForm(instance=profile)
purge_form = PurgeForm()
seen_by = MetaViews.objects.filter(target_user=user).distinct().order_by('-id')[:10]
has_seen = MetaViews.objects.filter(from_user=user).distinct().order_by('-id')[:10]
@@ -1699,6 +1765,9 @@ def user_tools(request, username):
return render(request, 'closedverse_main/man/usertools.html', {
'title': 'Admin tools',
'user': user,
'user_form': user_form,
'purge_form': purge_form,
'profile_form': profile_form,
'seen_by': seen_by,
'has_seen': has_seen,
'profile': profile,
@@ -1710,16 +1779,16 @@ def user_tools_meta(request, username):
if not request.user.is_authenticated:
raise Http404()
if not request.user.can_manage():
raise Http404()
return HttpResponseForbidden()
if request.user.level < settings.min_lvl_metadata_perms:
raise Http404()
return HttpResponseForbidden()
user = get_object_or_404(User, username__iexact=username)
profile = user.profile()
if user.protect_data:
return json_response('This user\'s data has been locked.')
return HttpResponseForbidden()
# check if the requesting user is allowed to view someone
if user.has_authority(request.user):
raise Http404()
return HttpResponseForbidden()
# get the last time the page was opened
last_opened = MetaViews.objects.filter(target_user=user, from_user=request.user).order_by('-created').first()
@@ -1756,58 +1825,43 @@ def user_tools_meta(request, username):
def user_tools_set(request, username):
if request.method == 'POST':
user = get_object_or_404(User, username__iexact=username)
profile = user.profile()
if not request.user.is_authenticated:
raise Http404()
if not request.user.can_manage():
raise Http404()
return HttpResponseForbidden()
# obtain instance of user and profile
user = get_object_or_404(User, username__iexact=username)
profile = user.profile()
if user.has_authority(request.user):
raise Http404()
if request.POST.get('username') == "" or None:
return json_response('Username Invalid')
if not re.compile(r'^[A-Za-z0-9-._]{1,32}$').match(request.POST['username']) or not re.compile(r'[A-Za-z0-9]').match(request.POST['username']):
return json_response("The username either contains invalid characters or is too long (only letters + numbers, dashes, dots and underscores are allowed")
if User.objects.filter(username=request.POST['username']).exists() and not request.POST['username'] == user.username:
return json_response("Username is taken, please pick a new name.")
if request.POST.get('nickname') == "" or None:
return json_response('Nickname Invalid')
if request.POST.get('post_limit') == "" or None:
return json_response('Post limit Invalid')
postlimitint = int(request.POST.get('post_limit'))
if postlimitint <= -1 or postlimitint >= 999:
return json_response('Post limit Invalid')
return HttpResponseForbidden()
user.warned_reason = (request.POST.get('warned_reason') or None)
user.username = request.POST.get('username')
profile.comment = request.POST.get('profile_comment')
user.nickname = request.POST.get('nickname')
profile.limit_post = int(request.POST.get('post_limit'))
user.active = True if request.POST.get('active') is None else False
user.warned = False if request.POST.get('warned') is None else True
profile.let_freedom = True if request.POST.get('let_freedom') is None else False
profile.cannot_edit = False if request.POST.get('cannot_edit') is None else True
user.can_invite = True if request.POST.get('can_invite') is None else False
user_form = UserForm(request.POST, instance=user)
profile_form = ProfileForm(request.POST, instance=profile)
purge_form = PurgeForm(request.POST)
purge_posts = False if request.POST.get('purge_posts') is None else True
purge_comments = False if request.POST.get('purge_comments') is None else True
restore_content = False if request.POST.get('restore_content') is None else True
if purge_form.is_valid():
purge_posts = purge_form.cleaned_data["purge_posts"]
purge_comments = purge_form.cleaned_data["purge_comments"]
restore_content = purge_form.cleaned_data["restore_all"]
# Probably (still) a better way to do this, but it's here for now.
if restore_content == True:
if purge_comments or purge_posts:
return json_response('You cannot purge and restore at the same time.')
else:
Post.real.filter(creator=user, status=5, is_rm=True).update(is_rm=False, status=0)
Comment.real.filter(creator=user, status=5, is_rm=True).update(is_rm=False, status=0)
if purge_posts == True:
Post.real.filter(creator=user).update(is_rm=True, status=5)
if purge_comments == True:
Comment.real.filter(creator=user).update(is_rm=True, status=5)
if restore_content == True:
if purge_comments or purge_posts:
return json_response('You cannot purge and restore at the same time.')
else:
Post.real.filter(creator=user, status=5, is_rm=True).update(is_rm=False, status=0)
Comment.real.filter(creator=user, status=5, is_rm=True).update(is_rm=False, status=0)
if purge_posts == True:
Post.real.filter(creator=user).update(is_rm=True, status=5)
if purge_comments == True:
Comment.real.filter(creator=user).update(is_rm=True, status=5)
profile.save()
user.save()
AuditLog.objects.create(type=2, user=user, by=request.user, reasoning=request.POST)
return HttpResponse()
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
AuditLog.objects.create(type=2, user=user, by=request.user)
return HttpResponse()
else:
return json_response('Error.' + user_form.errors.as_text() + profile_form.errors.as_text())
else:
raise Http404()
@@ -1897,7 +1951,7 @@ def my_data(request):
log_attempt = LoginAttempt.objects.filter(user=user).order_by('-id')[:10]
history = ProfileHistory.objects.filter(user=user).order_by('-id')[:10]
creation_date = user.created.date()
datenow = date.today()
datenow = timezone.now().date()
age = datenow - creation_date
return render(request, 'closedverse_main/help/my-data.html', {
'user': user,
@@ -1943,11 +1997,16 @@ def change_password_set(request):
if not user.check_password(old):
return json_response('The old password specified does not match the user\'s password. Enter the password you use as of right now.')
# do the length check
if len(new) < settings.minimum_password_length:
return json_response('The new password must be at least ' + str(settings.minimum_password_length) + ' characters long.')
#if len(new) < settings.minimum_password_length:
# return json_response('The new password must be at least ' + str(settings.minimum_password_length) + ' characters long.')
try:
validate_password(new, user=user)
except ValidationError as error:
return json_response(error)
# do the thing
user.set_password(new)
user.save()
update_session_auth_hash(request, user)
return json_response("Success! Now you can log in with your new password!")
else:
raise Http404