File sharing update (requires migrations for existing databases)

There are a bunch of migration files that have been thrown into the repository. Sadly, migrating is a huge pain in the fucking ass, you may need to rollback your database or god knows what.

- did not add change logs too lazy
This commit is contained in:
some weird guy
2023-10-16 15:22:41 -07:00
parent d49ce565ee
commit e5c99e0248
26 changed files with 942 additions and 1070 deletions
+4 -6
View File
@@ -4,7 +4,10 @@ Stuff that is done:
- Hide email password reset form if no SMTP server is set up. (done by Arian Kordi) - Hide email password reset form if no SMTP server is set up. (done by Arian Kordi)
- We are now using django groups. - We are now using django groups.
- Admins have a better interface. - Admins have a better interface.
- Post limit shows publicly, with indication on how many posts are left. - Post limit shows for admins, with indication on how many posts are left.
- Image and video file boxes in one.
- Full ImageField integration.
Stuff that is in progress Stuff that is in progress
- New Warning system. - New Warning system.
@@ -17,18 +20,13 @@ Todo:
When banning someone, add the option to ban or revoke invites for whoever invited that person. When banning someone, add the option to ban or revoke invites for whoever invited that person.
- FIX THE FUCKING CONTACT PAGE (nah not doing that lmao) - FIX THE FUCKING CONTACT PAGE (nah not doing that lmao)
- also, CONTINUE MOVING OVER TO FORMS.PY - also, CONTINUE MOVING OVER TO FORMS.PY
Profile settings
Email reset form Email reset form
Post forms
Signup page Signup page
A bunch of other forms I haven't thought about. A bunch of other forms I haven't thought about.
Ideas: Ideas:
- An account approval system, we really need one. "is_new" exists in the user model, we just have to put that to use. - An account approval system, we really need one. "is_new" exists in the user model, we just have to put that to use.
- "Muting" feature to replace "active" - "Muting" feature to replace "active"
- Image and video file boxes in one.
- Make it, so you need to enter your current password to change your email address. - Make it, so you need to enter your current password to change your email address.
- Ways for mods to view who invited who easily. - Ways for mods to view who invited who easily.
- Full ImageField integration.
- Filefield may have to be used for both photos and videos.
- remove the useless feedback thing. (You can just make a bug reporting community) - remove the useless feedback thing. (You can just make a bug reporting community)
+5 -1
View File
@@ -1,6 +1,9 @@
# About # About
Cedar-Django is a fork of Closedverse with custom features added to it. Cedar-Django is a fork of Closedverse with custom features added to it.
### Upload anything you want.
You can upload whatever files you want to the site. The primary supported file types are Images, Videos and audio files. Other files will have a download button.
### Theme changing ### Theme changing
Just like in Indigo, you can change the color of your theme. While this is only visible to each user specifically, a global theme can be set in Settings.py. Just like in Indigo, you can change the color of your theme. While this is only visible to each user specifically, a global theme can be set in Settings.py.
@@ -33,6 +36,7 @@ Moderators and staff will be able to revoke a user's ability to add new users if
- Django 3.2.2 - Django 3.2.2
- urllib3 - urllib3
- lxml - lxml
- django-cleanup
- passlib - passlib
- bcrypt - bcrypt
- pillow - pillow
@@ -59,7 +63,7 @@ You need Pip
4. 4.
Get everything else you need. Get everything else you need.
`pip3 install Django==3.2.2 urllib3 lxml passlib bcrypt pillow django-markdown-deux django-markdown2 whitenoise django-xff` `pip3 install Django==3.2.2 urllib3 lxml passlib bcrypt pillow django-markdown-deux django-markdown2 whitenoise django-xff django-cleanup`
5. 5.
Clone the clone! Clone the clone!
+7 -12
View File
@@ -29,7 +29,7 @@ DEBUG = False
# Do not include 127.0.0.1 or localhost # Do not include 127.0.0.1 or localhost
# in production for security reasons. # in production for security reasons.
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
'', '127.0.0.1',
] ]
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
@@ -48,6 +48,8 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'markdown_deux', 'markdown_deux',
'closedverse_main', 'closedverse_main',
# Comment this out to disable file cleanup.
'django_cleanup.apps.CleanupConfig',
] ]
X_FRAME_OPTIONS='SAMEORIGIN' X_FRAME_OPTIONS='SAMEORIGIN'
@@ -229,12 +231,11 @@ LOGIN_EXEMPT_URLS = {
r'^help/rules$', r'^help/rules$',
r'^help/contact$', r'^help/contact$',
r'^help/login$', r'^help/login$',
r'^s/.*$',
} }
# Action to perform on images belonging to posts/ # The max file size allowed. (default: 10*1024*1024")
# comments when they are deleted max_file_size = 10*1024*1024
# 0: keep, 1: move to 'rm' folder, 2: delete
IMAGE_DELETE_SETTING = 2
# allow sign ups. # allow sign ups.
allow_signups = True allow_signups = True
@@ -253,14 +254,8 @@ level_needed_to_man_communities = 5
# if someone's level is equal or above this, they can edit any user with a lower level on your clone. # if someone's level is equal or above this, they can edit any user with a lower level on your clone.
level_needed_to_man_users = 5 level_needed_to_man_users = 5
# minimum_password_length is removed as the AUTH_PASSWORD_VALIDATORS work as of 08/2023
# The hard limit for uploading, Will cause an error if this is exceeded. This is set to 15MB by default (15728640)
DATA_UPLOAD_MAX_MEMORY_SIZE = 52428800
# Set the default theme here! Set this to None to use the normal Closedverse theme. # Set the default theme here! Set this to None to use the normal Closedverse theme.
# TODO, make this work for users who aren't logged in. site_wide_theme_hex = None
site_wide_theme_hex = 'ff4159'
# age (minimal 13 due to C.O.P.P.A) # age (minimal 13 due to C.O.P.P.A)
age_allowed = "13" age_allowed = "13"
+4 -4
View File
@@ -56,7 +56,7 @@ class UserAdmin(BaseUserAdmin):
fieldsets = ( fieldsets = (
(None, {'fields': ('nickname', 'username', 'password')}), (None, {'fields': ('nickname', 'username', 'password')}),
('Personal info', {'fields': ('email', ('addr', 'signup_addr'))}), ('Personal info', {'fields': ('email', ('addr', 'signup_addr'))}),
('Cosmetic', {'fields': (('role', 'avatar', 'has_mh'), 'color', 'theme', 'bg_url',)}), ('Cosmetic', {'fields': (('role', 'avatar_type', 'avatar_input', 'avatar_upload'), 'color', 'theme')}),
('Data', {'fields': ('last_login', 'created', 'hide_online')}), ('Data', {'fields': ('last_login', 'created', 'hide_online')}),
('Permissions', {'fields': (('is_active', 'is_staff', 'is_superuser', 'can_invite'), 'level', 'c_tokens', 'groups', 'user_permissions')}), ('Permissions', {'fields': (('is_active', 'is_staff', 'is_superuser', 'can_invite'), 'level', 'c_tokens', 'groups', 'user_permissions')}),
) )
@@ -132,7 +132,7 @@ class ConversationAdmin(admin.ModelAdmin):
class PostAdmin(admin.ModelAdmin): class PostAdmin(admin.ModelAdmin):
raw_id_fields = ('creator', 'poll', 'community', ) raw_id_fields = ('creator', 'poll', 'community', )
search_fields = ('id', 'body', 'creator__username', ) search_fields = ('id', 'body', 'creator__username', )
list_display = ('id', 'created', 'creator', 'body', 'is_rm', ) list_display = ('id', 'created', 'creator', 'body', 'file', 'is_rm', )
list_filter = ('is_rm', 'created', ) list_filter = ('is_rm', 'created', )
actions = [Hide_content, Show_content, Disable_comments, Enable_comments] actions = [Hide_content, Show_content, Disable_comments, Enable_comments]
def get_queryset(self, request): def get_queryset(self, request):
@@ -141,7 +141,7 @@ class PostAdmin(admin.ModelAdmin):
class CommentAdmin(admin.ModelAdmin): class CommentAdmin(admin.ModelAdmin):
raw_id_fields = ('creator', 'original_post', 'community', ) raw_id_fields = ('creator', 'original_post', 'community', )
search_fields = ('id', 'body', 'creator__username', ) search_fields = ('id', 'body', 'creator__username', )
list_display = ('id', 'created', 'creator', 'body', 'original_post', 'is_rm', ) list_display = ('id', 'created', 'creator', 'body', 'file', 'original_post', 'is_rm', )
list_filter = ('is_rm', 'created', ) list_filter = ('is_rm', 'created', )
actions = [Hide_content, Show_content] actions = [Hide_content, Show_content]
def get_queryset(self, request): def get_queryset(self, request):
@@ -159,7 +159,7 @@ class CommunityAdmin(admin.ModelAdmin):
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):
raw_id_fields = ('creator', 'conversation', ) raw_id_fields = ('creator', 'conversation', )
search_fields = ('id', 'body', 'creator__username', ) search_fields = ('id', 'body', 'creator__username', )
list_display = ('id', 'created', 'creator', 'conversation', 'body', 'read', 'is_rm', ) list_display = ('id', 'created', 'creator', 'conversation', 'body', 'file', 'read', 'is_rm', )
list_filter = ('is_rm', 'read', 'created', ) list_filter = ('is_rm', 'read', 'created', )
actions = [Hide_content, Show_content] actions = [Hide_content, Show_content]
def get_queryset(self, request): def get_queryset(self, request):
+254 -35
View File
@@ -7,14 +7,16 @@ from django.core.files.base import ContentFile
from django.contrib.auth.password_validation import validate_password from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.timezone import timedelta from django.utils.timezone import timedelta
from django.core.validators import URLValidator
from closedverse import settings from closedverse import settings
from django.core.validators import EmailValidator
# will be used for community icons and profile pictures def compress_and_resize_content(image, icon=False):
def compress_and_resize_icon(image): try:
im = Image.open(image) im = Image.open(image)
im = im.convert('RGB') # Convert to RGB im = im.convert('RGB')
# Crop to 1:1 aspect ratio if icon == True:
width, height = im.size width, height = im.size
min_dimension = min(width, height) min_dimension = min(width, height)
left = (width - min_dimension) / 2 left = (width - min_dimension) / 2
@@ -22,52 +24,129 @@ def compress_and_resize_icon(image):
right = (width + min_dimension) / 2 right = (width + min_dimension) / 2
bottom = (height + min_dimension) / 2 bottom = (height + min_dimension) / 2
im = im.crop((left, top, right, bottom)) im = im.crop((left, top, right, bottom))
# Resize to 100 by 100 or smaller
im.thumbnail((100, 100)) im.thumbnail((100, 100))
else:
# Compress the image
output = io.BytesIO()
im.save(output, format='WEBP', quality=85)
output.seek(0)
random_name = f"{uuid.uuid4()}.webp"
return ContentFile(output.read(), name=random_name)
def compress_and_resize_content(image):
im = Image.open(image)
im = im.convert('RGB')
# Resize to 1,200 by 1,200 or smaller
im.thumbnail((1200, 1200)) im.thumbnail((1200, 1200))
# Compress the image
output = io.BytesIO() output = io.BytesIO()
im.save(output, format='WEBP', quality=85) # no more webp
im.save(output, format='JPEG', quality=85)
output.seek(0) output.seek(0)
random_name = f"{uuid.uuid4()}.webp" random_name = f"{uuid.uuid4()}.jpg"
return ContentFile(output.read(), name=random_name) return ContentFile(output.read(), name=random_name)
except:
return image
# I do want to move each and every form over to here. Not only will this trivialize making new forms, this will also make it more secure or something. class message_form(forms.ModelForm):
class CommunitySettingForm(forms.ModelForm): body = forms.CharField(max_length=2200, required=True)
file = forms.FileField(required=False)
feeling_id = forms.IntegerField(required=False)
def clean_file(self):
file = self.cleaned_data.get('file')
max_size = settings.max_file_size
if file:
if file.size > max_size:
raise ValidationError(f'File size is too large. ({max_size / 1024 /1024 }MB max)')
if file.content_type.startswith('image'):
return compress_and_resize_content(image=file)
return file
class Meta:
model = Message
fields = (
'body',
'file',
'feeling_id',
)
class comment_form(forms.ModelForm):
body = forms.CharField(max_length=2200, required=True)
file = forms.FileField(required=False)
feeling_id = forms.IntegerField(required=False)
is_spoiler = forms.BooleanField(required=False)
def clean_file(self):
file = self.cleaned_data.get('file')
max_size = settings.max_file_size
if file:
if file.size > max_size:
raise ValidationError(f'File size is too large. ({max_size / 1024 /1024 }MB max)')
if file.content_type.startswith('image'):
return compress_and_resize_content(image=file)
return file
class Meta:
model = Comment
fields = (
'body',
'file',
'feeling_id',
'is_spoiler',
)
class post_form(forms.ModelForm):
body = forms.CharField(max_length=2200, required=True)
url = forms.URLField(required=False)
file = forms.FileField(required=False)
feeling_id = forms.IntegerField(required=False)
is_spoiler = forms.BooleanField(required=False)
def clean_file(self):
file = self.cleaned_data.get('file')
max_size = settings.max_file_size
if file:
if file.size > max_size:
raise ValidationError(f'File size is too large. ({max_size / 1024 /1024 }MB max)')
if file.content_type.startswith('image'):
return compress_and_resize_content(image=file)
return file
def clean_url(self):
url = self.cleaned_data.get('url')
if url:
try:
URLValidator()(value=url)
except ValidationError:
raise ValidationError("Uh-oh, that URL wasn't valid.")
return url
class Meta:
model = Post
fields = (
'body',
'url',
'file',
'feeling_id',
'is_spoiler',
)
class edit_community(forms.ModelForm):
description = forms.CharField(max_length = 2200,required=False, widget=forms.Textarea(attrs={'class': 'textarea'})) description = forms.CharField(max_length = 2200,required=False, widget=forms.Textarea(attrs={'class': 'textarea'}))
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(CommunitySettingForm, self).__init__(*args, **kwargs) super(edit_community, self).__init__(*args, **kwargs)
# Store the initial values of the image fields # Store the initial values of the image fields
self.initial_ico = self.instance.ico self.initial_ico = self.instance.ico
self.initial_banner = self.instance.banner self.initial_banner = self.instance.banner
def clean_ico(self): def clean_ico(self):
ico = self.cleaned_data.get('ico') ico = self.cleaned_data.get('ico')
max_size = settings.max_file_size
# Check if the image has changed # Check if the image has changed
if ico and ico != self.initial_ico: if ico and ico != self.initial_ico:
return compress_and_resize_icon(image=ico) if ico.size > max_size:
raise ValidationError(f'File size is too large. ({max_size / 1024 /1024 }MB max)')
return compress_and_resize_content(image=ico, icon=True)
return ico return ico
def clean_banner(self): def clean_banner(self):
banner = self.cleaned_data.get('banner') banner = self.cleaned_data.get('banner')
max_size = settings.max_file_size
# Check if the image has changed # Check if the image has changed
if banner and banner != self.initial_banner: if banner and banner != self.initial_banner:
if banner.size > max_size:
raise ValidationError(f'File size is too large. ({max_size / 1024 /1024 }MB max)')
return compress_and_resize_content(image=banner) return compress_and_resize_content(image=banner)
return banner return banner
@@ -82,7 +161,7 @@ class CommunitySettingForm(forms.ModelForm):
'banner', 'banner',
) )
class Settomgs_Change_Password(forms.Form): class set_password(forms.Form):
Old_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Old Password'})) Old_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Old Password'}))
New_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'New Password'})) New_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'New Password'}))
Confirm_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Confirm Password'})) Confirm_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Confirm Password'}))
@@ -103,17 +182,17 @@ class Settomgs_Change_Password(forms.Form):
except forms.ValidationError as error: except forms.ValidationError as error:
raise forms.ValidationError("your password fucking sucks!") raise forms.ValidationError("your password fucking sucks!")
return cleaned_data return cleaned_data
class PurgeForm(forms.Form): class purge_user(forms.Form):
purge_posts = forms.BooleanField(required=False, label='Purge all posts', help_text='Purge all posts from this user.') purge_posts = forms.BooleanField(required=False, label='Purge all posts', help_text='Purge all posts from this user.')
purge_comments = forms.BooleanField(required=False, label='Purge all comments', help_text='Purge all comments from this user.') purge_comments = forms.BooleanField(required=False, label='Purge all comments', help_text='Purge all comments from this user.')
restore_all = forms.BooleanField(required=False, label='Restore purged content', help_text='Restore everything that was purged, this will not apply to posts deleted manually.') restore_all = forms.BooleanField(required=False, label='Restore purged content', help_text='Restore everything that was purged, this will not apply to posts deleted manually.')
class LoginForm(forms.Form): class sign_in(forms.Form):
username = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'class': 'auth-input', 'placeholder': 'Username / Email'})) username = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'class': 'auth-input', 'placeholder': 'Username / Email'}))
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Password'})) password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Password'}))
def clean(self): def clean(self):
cleaned_data = super(LoginForm, self).clean() cleaned_data = super(sign_in, self).clean()
username = cleaned_data.get('username') username = cleaned_data.get('username')
password = cleaned_data.get('password') password = cleaned_data.get('password')
@@ -131,9 +210,10 @@ class LoginForm(forms.Form):
active_user_ban = Ban.objects.filter(to=user[0], active=True, expiry_date__gte=timezone.now()).first() active_user_ban = Ban.objects.filter(to=user[0], active=True, expiry_date__gte=timezone.now()).first()
if active_user_ban: if active_user_ban:
raise forms.ValidationError("This account has been banned until {}. Reason: {}".format(active_user_ban.expiry_date, active_user_ban.reason), code='banned') raise forms.ValidationError("This account has been banned until {}. Reason: {}".format(active_user_ban.expiry_date, active_user_ban.reason), code='banned')
self.user = user
return cleaned_data return cleaned_data
class User_tools_Form(forms.ModelForm): class manage_user(forms.ModelForm):
username = forms.CharField(max_length = 64, required = True, help_text = 'Usernames are used when signing in, so be sure to let this user know if you change this.') username = forms.CharField(max_length = 64, required = True, help_text = 'Usernames are used when signing in, so be sure to let this user know if you change this.')
has_mh = forms.BooleanField(required = False, label='Use Mii', help_text='Turn this on to use Mii Hashes instead of normal profile pictures.') has_mh = forms.BooleanField(required = False, label='Use Mii', help_text='Turn this on to use Mii Hashes instead of normal profile pictures.')
avatar = forms.CharField(required = False, help_text = 'If \"Use Mii\" is turned on, The Mii Hash should reside here, otherwise any good old URL will do.') avatar = forms.CharField(required = False, help_text = 'If \"Use Mii\" is turned on, The Mii Hash should reside here, otherwise any good old URL will do.')
@@ -148,7 +228,7 @@ class User_tools_Form(forms.ModelForm):
raise forms.ValidationError("The username contains invalid characters.") raise forms.ValidationError("The username contains invalid characters.")
return username return username
class Profile_tools_Form(forms.ModelForm): class manage_profile(forms.ModelForm):
# applying classes is super weird. # applying classes is super weird.
comment = forms.CharField(required=False, widget=forms.Textarea(attrs={'class': 'textarea'})) comment = forms.CharField(required=False, widget=forms.Textarea(attrs={'class': 'textarea'}))
let_freedom = forms.BooleanField(required=False, label='Let this user upload images?', help_text='If you disable this, This user will not be able to post images, videos or make a new account.') let_freedom = forms.BooleanField(required=False, label='Let this user upload images?', help_text='If you disable this, This user will not be able to post images, videos or make a new account.')
@@ -156,22 +236,24 @@ class Profile_tools_Form(forms.ModelForm):
model = Profile model = Profile
fields = ['comment', 'country', 'whatareyou', 'weblink', 'external', 'let_freedom', 'limit_post', 'cannot_edit'] fields = ['comment', 'country', 'whatareyou', 'weblink', 'external', 'let_freedom', 'limit_post', 'cannot_edit']
class Give_warning_form(forms.ModelForm): class give_warning(forms.ModelForm):
reason = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'textarea'})) reason = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'textarea'}))
# Super simple, does not need to be it's own form but whatever. # Super simple, does not need to be it's own form but whatever.
class Meta: class Meta:
model = Warning model = Warning
fields = ['reason'] fields = ['reason']
class Give_Ban_Form(forms.ModelForm): class give_ban(forms.ModelForm):
BAN_OPTIONS = [ BAN_OPTIONS = [
(1, '1 Day'), (1, '1 Day'),
(2, '2 Days'), (2, '2 Days'),
(3, '3 Days'), (3, '3 Days'),
(7, '1 Week'), (7, '1 Week'),
(14, '2 Weeks'), (14, '2 Weeks'),
(21, '3 Weeks'),
(30, '1 Month'), (30, '1 Month'),
(60, '2 Months'), (60, '2 Months'),
(90, '3 Months'),
(365, '1 Year'), (365, '1 Year'),
(None, 'Forever'), (None, 'Forever'),
] ]
@@ -189,10 +271,147 @@ class Give_Ban_Form(forms.ModelForm):
model = Ban model = Ban
fields = ['reason', 'expiry_date'] fields = ['reason', 'expiry_date']
class Give_Ban_Form_Edit(forms.ModelForm): class edit_ban(forms.ModelForm):
reason = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'textarea'})) reason = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'textarea'}))
expiry_date = forms.DateTimeField(widget=forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'DateTimeInput'}, format='%Y-%m-%dT%H:%M')) expiry_date = forms.DateTimeField(widget=forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'DateTimeInput'}, format='%Y-%m-%dT%H:%M'))
active = forms.BooleanField(required=False) active = forms.BooleanField(required=False)
class Meta: class Meta:
model = Ban model = Ban
fields = ['reason', 'expiry_date', 'active'] fields = ['reason', 'expiry_date', 'active']
class profile_settings_page(forms.Form):
# This form is an absolute clusterfuck.
screen_name = forms.CharField(max_length=32, required=True)
pronouns = forms.CharField(max_length=16, required=False)
profile_comment = forms.CharField(max_length=2200, required=False)
country = forms.CharField(max_length=64, required=False)
email = forms.CharField(max_length=255, required=False)
website = forms.CharField(max_length=255, required=False)
external = forms.CharField(max_length=255, required=False)
whatareyou = forms.CharField(max_length=255, required=False)
color = forms.CharField(max_length=7, required=False)
id_visibility = forms.IntegerField(max_value=2, min_value=0)
let_friendrequest = forms.IntegerField(max_value=2, min_value=0)
yeahs_visibility = forms.IntegerField(max_value=2, min_value=0)
comments_visibility = forms.IntegerField(max_value=2, min_value=0)
theme = forms.CharField(max_length=7, required=False)
reset_theme = forms.BooleanField(required=False)
'''PFP stuff'''
avatar = forms.IntegerField(max_value=3, min_value=0, required=False)
origin_id = forms.CharField(max_length=16, min_length=6, required=False)
mh = forms.CharField(max_length=16, required=False)
file= forms.ImageField(required=False)
def clean_email(self):
email = self.cleaned_data.get('email')
validator = EmailValidator()
if email:
try:
validator(email)
except ValidationError:
raise forms.ValidationError("Invalid email.")
return email
return None
def is_color_acceptable(self, hex_color):
hex_color = hex_color.lstrip('#')
r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
r /= 255.0
g /= 255.0
b /= 255.0
luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
# Define thresholds
too_dark_threshold = 0.2
too_bright_threshold = 0.8
if luminance < too_dark_threshold:
return False
elif luminance > too_bright_threshold:
return False
else:
return True
def get_gravatar(self, email):
try:
page = urllib.request.urlopen('https://gravatar.com/avatar/'+ md5(email.encode('utf-8').lower()).hexdigest() +'?d=404&s=128')
except:
return False
return page.geturl()
# what an abomination
def save(self, user, commit=True):
profile = user.profile()
user.nickname = self.cleaned_data['screen_name']
user.email = self.cleaned_data['email']
if not self.cleaned_data.get('origin_id'):
profile.origin_info = None
profile.origin_id = None
avatar_option = self.cleaned_data.get('avatar')
match avatar_option:
# will add case 3 later who gives a shit
case 2:
if self.cleaned_data.get('file'):
user.avatar_upload = compress_and_resize_content(image=self.cleaned_data.get('file'), icon=True)
user.avatar_type = 0
case 1:
user.avatar_input = util.get_gravatar(user.email) or None
user.avatar_type = 1
case 0:
if not self.cleaned_data.get('origin_id'):
user.avatar_type = 0
user.avatar_input = None
else:
if self.cleaned_data.get('mh'):
user.avatar_type = 2
user.avatar_input = self.cleaned_data.get('mh')
profile.origin_id = self.cleaned_data.get('origin_id')
profile.origin_info = json.dumps([self.cleaned_data.get('mh'), 'if you see this then something is wrong', self.cleaned_data['origin_id']])
# Setting the username color
color = self.cleaned_data.get('color')
if color:
try:
validate_color(color)
except ValidationError:
user.color = None
else:
if self.is_color_acceptable(color):
user.color = color
else:
user.color = None
# Setting the theme
theme = self.cleaned_data.get('theme')
if theme:
reset_theme = self.cleaned_data.get('reset_theme')
try:
validate_color(theme)
except ValidationError:
user.theme = None
else:
if reset_theme:
user.theme = None
pass
if self.is_color_acceptable(theme):
user.theme = theme
else:
user.theme = None
profile.comment = self.cleaned_data['profile_comment']
profile.pronoun_is = self.cleaned_data['pronouns']
profile.country = self.cleaned_data['country']
profile.weblink = self.cleaned_data['website']
profile.external = self.cleaned_data['external']
profile.whatareyou = self.cleaned_data['whatareyou']
profile.id_visibility = self.cleaned_data['id_visibility']
profile.let_friendrequest = self.cleaned_data['let_friendrequest']
profile.yeahs_visibility = self.cleaned_data['yeahs_visibility']
profile.comments_visibility = self.cleaned_data['comments_visibility']
if commit:
user.save()
profile.save()
@@ -0,0 +1,33 @@
# Generated by Django 3.2.2 on 2023-10-16 20:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('admin', '0003_logentry_add_action_flag_choices'),
('closedverse_main', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='userrequest',
name='user_ptr',
),
migrations.AlterModelOptions(
name='user',
options={},
),
migrations.AlterField(
model_name='user',
name='is_staff',
field=models.BooleanField(default=False, help_text="Allow this user to access the admin panel you're using right now? Don't forget to specify the permissions."),
),
migrations.DeleteModel(
name='ConversationInvite',
),
migrations.DeleteModel(
name='UserRequest',
),
]
@@ -0,0 +1,49 @@
# Generated by Django 3.2.2 on 2023-10-16 21:10
import closedverse_main.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('closedverse_main', '0002_auto_20231016_1355'),
]
operations = [
migrations.AddField(
model_name='comment',
name='file',
field=models.FileField(blank=True, max_length=1024, null=True, upload_to='comment_file/%Y/%m/%d/'),
),
migrations.AddField(
model_name='message',
name='file',
field=models.FileField(blank=True, max_length=1024, null=True, upload_to='message_file/%Y/%m/%d/'),
),
migrations.AddField(
model_name='post',
name='file',
field=models.FileField(blank=True, max_length=1024, null=True, upload_to='post_file/%Y/%m/%d/'),
),
migrations.AddField(
model_name='user',
name='avatar_input',
field=models.CharField(blank=True, help_text='Input a Mii Hash, or URL depending on what "Avatar type" is set to..', max_length=1200, null=True),
),
migrations.AddField(
model_name='user',
name='avatar_type',
field=models.SmallIntegerField(choices=[(0, 'ImageField'), (1, 'URL / Gravatar'), (2, 'Mii Hash'), (3, 'Mii Studio')], default=False, help_text='Determines the type of avatar this user has.'),
),
migrations.AddField(
model_name='user',
name='avatar_upload',
field=models.ImageField(blank=True, help_text='Note this only works if you have "Avatar type" set to "ImageField"', null=True, upload_to='avatars/'),
),
migrations.AddField(
model_name='user',
name='show_announcements',
field=models.BooleanField(default=True),
),
]
@@ -0,0 +1,60 @@
# Generated by Django 3.2.2 on 2023-10-16 21:19
from django.db import migrations
def migrate_media_to_file(apps, schema_editor):
# Migrate Comment model
Comment = apps.get_model('closedverse_main', 'Comment')
for comment in Comment.objects.all():
if comment.drawing and comment.drawing.startswith('/i/'):
comment.file = comment.drawing[3:]
comment.save()
if comment.screenshot and comment.screenshot.startswith('/i/'):
comment.file = comment.screenshot[3:]
comment.save()
# Migrate Message model
Message = apps.get_model('closedverse_main', 'Message')
for message in Message.objects.all():
if message.drawing and message.drawing.startswith('/i/'):
message.file = message.drawing[3:]
message.save()
if message.screenshot and message.screenshot.startswith('/i/'):
message.file = message.screenshot[3:]
message.save()
# Migrate Post model
Post = apps.get_model('closedverse_main', 'Post')
for post in Post.objects.all():
if post.drawing and post.drawing.startswith('/i/'):
post.file = post.drawing[3:]
post.save()
if post.screenshot and post.screenshot.startswith('/i/'):
post.file = post.screenshot[3:]
post.save()
if post.video and post.video.startswith('/i/'):
post.file = post.video[3:]
post.save()
User = apps.get_model('closedverse_main', 'User')
for user in User.objects.all():
user.avatar_input = user.avatar
if user.has_mh:
user.avatar_type = 2
else:
user.avatar_type = 1
user.save()
class Migration(migrations.Migration):
dependencies = [
('closedverse_main', '0003_auto_20231016_1410'),
]
operations = [
migrations.RunPython(migrate_media_to_file),
]
@@ -0,0 +1,113 @@
# Generated by Django 3.2.2 on 2023-10-16 22:02
import closedverse_main.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('closedverse_main', '0004_auto_20231016_1419'),
]
operations = [
migrations.RemoveField(
model_name='comment',
name='drawing',
),
migrations.RemoveField(
model_name='comment',
name='screenshot',
),
migrations.RemoveField(
model_name='message',
name='drawing',
),
migrations.RemoveField(
model_name='message',
name='screenshot',
),
migrations.RemoveField(
model_name='post',
name='drawing',
),
migrations.RemoveField(
model_name='post',
name='screenshot',
),
migrations.RemoveField(
model_name='post',
name='video',
),
migrations.RemoveField(
model_name='profile',
name='email_login',
),
migrations.RemoveField(
model_name='user',
name='avatar',
),
migrations.RemoveField(
model_name='user',
name='bg_url',
),
migrations.RemoveField(
model_name='user',
name='has_mh',
),
migrations.AlterField(
model_name='community',
name='banner',
field=models.ImageField(blank=True, null=True, upload_to='community_banners/'),
),
migrations.AlterField(
model_name='community',
name='ico',
field=models.ImageField(blank=True, null=True, upload_to='community_icons/'),
),
migrations.AlterField(
model_name='message',
name='url',
field=models.URLField(blank=True, max_length=1200, null=True),
),
migrations.AlterField(
model_name='profile',
name='comment',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='profile',
name='country',
field=models.CharField(blank=True, max_length=120),
),
migrations.AlterField(
model_name='profile',
name='external',
field=models.CharField(blank=True, max_length=255),
),
migrations.AlterField(
model_name='profile',
name='pronoun_is',
field=models.CharField(blank=True, max_length=16),
),
migrations.AlterField(
model_name='profile',
name='weblink',
field=models.CharField(blank=True, max_length=1200),
),
migrations.AlterField(
model_name='profile',
name='whatareyou',
field=models.CharField(blank=True, max_length=120),
),
migrations.AlterField(
model_name='user',
name='color',
field=closedverse_main.models.ColorField(blank=True, max_length=18, null=True),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(blank=True, max_length=254, null=True),
),
]
+130 -210
View File
@@ -17,7 +17,7 @@ import uuid, json, base64
from django.core.mail import send_mail from django.core.mail import send_mail
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.db.models.signals import pre_delete import mimetypes
import re import re
import unicodedata import unicodedata
import random import random
@@ -52,14 +52,14 @@ class UserManager(BaseUserManager):
) )
profile = Profile.objects.model() profile = Profile.objects.model()
if nn: if nn:
user.avatar = nn[0] user.avatar_input = nn[0]
profile.origin_id = nn[2] profile.origin_id = nn[2]
profile.origin_info = json.dumps(nn) profile.origin_info = json.dumps(nn)
user.has_mh = True user.avatar_type = 2
else: else:
user.avatar = util.get_gravatar(email) or ('s' if getrandbits(1) else '') user.avatar_input = util.get_gravatar(email) or ('s' if getrandbits(1) else '')
user.has_mh = False user.avatar_type = 1
user.set_password(password) user.set_password(password)
user.save(using=self._db) user.save(using=self._db)
profile.user = user profile.user = user
@@ -88,10 +88,6 @@ class UserManager(BaseUserManager):
user = user.first() user = user.first()
# If the user is an admin, say that they don't exist, actually no... # If the user is an admin, say that they don't exist, actually no...
# Or, if the user doesn't want username login, don't let them if they didn't enter their email # Or, if the user doesn't want username login, don't let them if they didn't enter their email
if user.profile('email_login') == 2 and not user.email == username:
return None
elif user.profile('email_login') == 0 and user.email == username:
return None
try: try:
passwd = user.check_password(password) passwd = user.check_password(password)
# Check if the password is a valid bcrypt # Check if the password is a valid bcrypt
@@ -136,15 +132,17 @@ class Role(models.Model):
# as of writing, mii-secure is unstable, nintendo please do not f*ck me for this # as of writing, mii-secure is unstable, nintendo please do not f*ck me for this
mii_domain = 'https://s3.us-east-1.amazonaws.com/mii-images.account.nintendo.net/' mii_domain = 'https://s3.us-east-1.amazonaws.com/mii-images.account.nintendo.net/'
studiomii_domain = 'https://studio.mii.nintendo.com/'
class User(AbstractBaseUser, PermissionsMixin): class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
username = models.CharField(max_length=32, unique=True) username = models.CharField(max_length=32, unique=True)
nickname = models.CharField(max_length=64, null=True) nickname = models.CharField(max_length=64, null=True)
password = models.CharField(max_length=128) password = models.CharField(max_length=128)
email = models.EmailField(null=True, blank=True, default='') email = models.EmailField(null=True, blank=True)
has_mh = models.BooleanField(default=False, help_text='Don\'t touch this. This is if the user\'s avatar is set to a Mii.') avatar_type = models.SmallIntegerField(default=False, help_text='Determines the type of avatar this user has.', choices=((0, 'ImageField'), (1, 'URL / Gravatar'), (2, 'Mii Hash'), (3, 'Mii Studio')))
avatar = models.CharField(max_length=1200, blank=True, default='') avatar_input = models.CharField(max_length=1200, null=True, blank=True, help_text='Input a Mii Hash, or URL depending on what "Avatar type" is set to..')
theme = ColorField(blank=True, null=True) avatar_upload = models.ImageField(blank=True, null=True, upload_to='avatars/', help_text='Note this only works if you have "Avatar type" set to "ImageField"')
# LEVEL: 0-1 is default, everything else is just levels # LEVEL: 0-1 is default, everything else is just levels
level = models.SmallIntegerField(default=0, help_text='This is the level of authority. People with a lower level cannot edit those with a higher level. This also grants additional permissions outside of the Django Admin Panel.') level = models.SmallIntegerField(default=0, help_text='This is the level of authority. People with a lower level cannot edit those with a higher level. This also grants additional permissions outside of the Django Admin Panel.')
role = models.ForeignKey(Role, blank=True, null=True, on_delete=models.SET_NULL, help_text='This will show a funny badge and text on this user\'s profile. This does not grant the user any additional power and is only visual.') role = models.ForeignKey(Role, blank=True, null=True, on_delete=models.SET_NULL, help_text='This will show a funny badge and text on this user\'s profile. This does not grant the user any additional power and is only visual.')
@@ -153,15 +151,16 @@ class User(AbstractBaseUser, PermissionsMixin):
# C Tokens are things that let you make communities and shit. # C Tokens are things that let you make communities and shit.
c_tokens = models.IntegerField(default=1, help_text='How many communities should this user be allowed to make?') c_tokens = models.IntegerField(default=1, help_text='How many communities should this user be allowed to make?')
# Things that don't have to do with auth lol # Personalization stuff
hide_online = models.BooleanField(default=False, help_text='If this is ticked, the user has opted to hide their online status.') hide_online = models.BooleanField(default=False, help_text='If this is ticked, the user has opted to hide their online status.')
color = ColorField(default='', null=True, blank=True) color = ColorField(null=True, blank=True)
theme = ColorField(blank=True, null=True)
show_announcements = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False, help_text='Allow this user to access the admin panel you\'re using right now? Don\'t forget to specify the permissions.') is_staff = models.BooleanField(default=False, help_text='Allow this user to access the admin panel you\'re using right now? Don\'t forget to specify the permissions.')
is_active = models.BooleanField(default=True, help_text='If this is off, the user is basically banned and can\'t do shit here') is_active = models.BooleanField(default=True, help_text='If this is off, the user is basically banned and can\'t do shit here')
is_superuser = models.BooleanField(default=False, help_text='Overrides django groups. This also allows you to change people\'s perms.') is_superuser = models.BooleanField(default=False, help_text='Overrides django groups. This also allows you to change people\'s perms.')
can_invite = models.BooleanField(default=True, help_text='Can this user invite new users? This does not matter unless the invite system is turned on.') can_invite = models.BooleanField(default=True, help_text='Can this user invite new users? This does not matter unless the invite system is turned on.')
bg_url = models.CharField(max_length=300, null=True, blank=True)
is_anonymous = False is_anonymous = False
is_authenticated = True is_authenticated = True
@@ -217,11 +216,10 @@ class User(AbstractBaseUser, PermissionsMixin):
return infodecode[0] return infodecode[0]
def unread_warning(self): def unread_warning(self):
return Notification.objects.filter(type=5, to=self, read=False).exists() return Notification.objects.filter(type=5, to=self, read=False).exists()
def has_plain_avatar(self):
if not self.has_mh and '/' in self.avatar and not 'gravatar.com' in self.avatar:
return True
def has_avatar(self): def has_avatar(self):
if not self.avatar or len(self.avatar) == 1: if self.avatar_type == 0 and not self.avatar_upload:
return False
if not self.avatar_type == 0 and not self.avatar_input:
return False return False
return True return True
def let_yeahnotifs(self): def let_yeahnotifs(self):
@@ -233,10 +231,12 @@ class User(AbstractBaseUser, PermissionsMixin):
return False return False
today_min = timezone.datetime.combine(timezone.datetime.today(), time.min) today_min = timezone.datetime.combine(timezone.datetime.today(), time.min)
today_max = timezone.datetime.combine(timezone.datetime.today(), time.max) today_max = timezone.datetime.combine(timezone.datetime.today(), time.max)
# recent_posts = # recent_posts
# Posts made by the user today + posts made by the IP today + recent_posts = Post.real.filter(
# same thing except with comments Q(creator=self) & Q(created__range=(today_min, today_max))
recent_posts = Post.real.filter(Q(creator=self.id, created__range=(today_min, today_max)) | Q(creator__addr=self.addr, created__range=(today_min, today_max))).count() + Comment.real.filter(Q(creator=self.id, created__range=(today_min, today_max)) | Q(creator__addr=self.addr, created__range=(today_min, today_max))).count() ).count() + Comment.real.filter(
Q(creator=self) & Q(created__range=(today_min, today_max))
).count()
# Posts remaining # Posts remaining
return int(limit) - recent_posts return int(limit) - recent_posts
@@ -268,7 +268,24 @@ class User(AbstractBaseUser, PermissionsMixin):
else: else:
return True return True
def do_avatar(self, feeling=0): def do_avatar(self, feeling=0):
if self.has_mh and self.avatar: anon = settings.STATIC_URL + 'img/anonymous-mii.png'
match self.avatar_type:
case 3:
if self.avatar_input:
feeling = {
0: 'normal',
1: 'smile_open_mouth',
2: 'like_wink_left',
3: 'surprise_open_mouth',
4: 'frustrated',
5: 'sorrow',
}.get(feeling, "normal")
url = '{2}miis/image.png?data={0}&type=face&expression={1}&width=128'.format(self.avatar_input, feeling, studiomii_domain)
return url
else:
return anon
case 2:
if self.avatar_input:
feeling = { feeling = {
0: 'normal', 0: 'normal',
1: 'happy', 1: 'happy',
@@ -277,15 +294,20 @@ class User(AbstractBaseUser, PermissionsMixin):
4: 'frustrated', 4: 'frustrated',
5: 'puzzled', 5: 'puzzled',
}.get(feeling, "normal") }.get(feeling, "normal")
url = '{2}{0}_{1}_face.png'.format(self.avatar, feeling, mii_domain) url = '{2}{0}_{1}_face.png'.format(self.avatar_input, feeling, mii_domain)
return url return url
elif not self.avatar:
return settings.STATIC_URL + 'img/anonymous-mii.png'
elif self.avatar == 's':
return settings.STATIC_URL + 'img/anonymous-mii-sad.png'
else: else:
return self.avatar return anon
case 1:
if self.avatar_input:
return self.avatar_input
else:
return anon
case 0:
if self.avatar_upload:
return self.avatar_upload.url
else:
return anon
def num_yeahs(self): def num_yeahs(self):
return self.yeah_set.count() return self.yeah_set.count()
def num_posts(self): def num_posts(self):
@@ -519,18 +541,12 @@ class User(AbstractBaseUser, PermissionsMixin):
return self.save(update_fields=['addr', 'last_login']) return self.save(update_fields=['addr', 'last_login'])
return self.save(update_fields=['last_login']) return self.save(update_fields=['last_login'])
def has_postspam(self, body, screenshot=None, drawing=None): def has_postspam(self, body, file=None):
latest_post = self.post_set.order_by('-created')[:1] latest_post = self.post_set.order_by('-created')[:1]
if not latest_post: if not latest_post:
return False return False
latest_post = latest_post.first() latest_post = latest_post.first()
if drawing and latest_post.drawing: if latest_post.body and body and not latest_post.file:
if drawing == latest_post.drawing:
return True
elif latest_post.screenshot and screenshot and not drawing:
if latest_post.screenshot == screenshot and latest_post.body == body:
return True
elif latest_post.body and body and not latest_post.screenshot and not latest_post.drawing:
if latest_post.body == body: if latest_post.body == body:
return True return True
return False return False
@@ -654,8 +670,8 @@ class Community(models.Model):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
description = models.TextField(blank=True) description = models.TextField(blank=True)
ico = models.ImageField(null=True, blank=True) ico = models.ImageField(null=True, blank=True, upload_to='community_icons/')
banner = models.ImageField(null=True, blank=True) banner = models.ImageField(null=True, blank=True, upload_to='community_banners/')
# Type: 0 - general, 1 - game, 2 - special # Type: 0 - general, 1 - game, 2 - special
type = models.SmallIntegerField(default=0, help_text='The category the community belongs in, setting this to None will remove the community.', choices=((0, 'General'), (1, 'Game'), (2, 'Special'), (3, 'User Community'), (4, 'Hide'))) type = models.SmallIntegerField(default=0, help_text='The category the community belongs in, setting this to None will remove the community.', choices=((0, 'General'), (1, 'Game'), (2, 'Special'), (3, 'User Community'), (4, 'Hide')))
platform = models.SmallIntegerField(default=0, choices=((0, 'None'), (1, '3DS'), (2, 'Wii U'), (3, 'Switch'), (4, '3DS and Wii U'), (5, 'PC'), (6, 'Xbox'), (7, 'Playstation'))) platform = models.SmallIntegerField(default=0, choices=((0, 'None'), (1, '3DS'), (2, 'Wii U'), (3, 'Switch'), (4, '3DS and Wii U'), (5, 'PC'), (6, 'Xbox'), (7, 'Playstation')))
@@ -788,67 +804,11 @@ class Community(models.Model):
if self.has_favorite(request): if self.has_favorite(request):
return request.user.communityfavorite_set.filter(community=self).delete() return request.user.communityfavorite_set.filter(community=self).delete()
def setup(self, request): def setup(self, request):
if request.user.is_authenticated: if request.user.is_authenticated:
self.post_perm = self.post_perm(request) self.post_perm = self.post_perm(request)
self.has_favorite = self.has_favorite(request) self.has_favorite = self.has_favorite(request)
def create_post(self, request):
if not self.post_perm(request):
return 4
limit = request.user.limit_remaining()
if not limit is False and not limit > 0:
return 8
del(limit)
#if Post.real.filter(Q(creator=request.user) | Q(creator__addr=request.user.addr), created__gt=timezone.now() - timedelta(seconds=10)).exists():
# return 3
if request.POST.get('url'):
try:
URLValidator()(value=request.POST['url'])
except ValidationError:
return 5
if not request.user.has_freedom() and (request.POST.get('url') or request.FILES.get('screen') or request.FILES.get('video')):
return 6
if not request.user.is_active:
return 6
if request.user.unread_warning():
return 13
if len(request.POST['body']) > 2200 or (len(request.POST['body']) < 1 and not request.POST.get('_post_type') == 'painting'):
return 1
upload = None
drawing = None
video = None
body = request.POST.get('body')
for c in body:
if unicodedata.combining(c):
return 12
if request.FILES.get('screen'):
upload = util.image_upload(request.FILES['screen'], True)
if upload == 1:
return 2
if request.FILES.get('video'):
video = util.video_upload(request.FILES['video'])
if video == 1:
return 11
if request.POST.get('_post_type') == 'painting':
if not request.POST.get('painting'):
return 2
drawing = util.image_upload(request.POST['painting'], False, True)
if drawing == 1:
return 2
# Check for spam using our OWN ALGO!!!!!!!!!
if request.user.has_postspam(body, upload, drawing):
return 7
for keyword in ['faggot', 'fag', 'nigger', 'nigga']:
if keyword in body.lower():
return 9
if body.isspace() and not drawing:
return 10
new_post = self.post_set.create(body=body, creator=request.user, community=self, feeling=int(request.POST.get('feeling_id', 0)), spoils=bool(request.POST.get('is_spoiler')), screenshot=upload, drawing=drawing, url=request.POST.get('url'), video=video)
new_post.is_mine = True
return new_post
def search(query='', limit=50, offset=0, request=None): def search(query='', limit=50, offset=0, request=None):
return Community.objects.filter(Q(name__icontains=query) | Q(description__contains=query)).exclude(type=4).order_by('-created')[offset:offset + limit] return Community.objects.filter(Q(name__icontains=query) | Q(description__contains=query)).exclude(type=4).order_by('-created')[offset:offset + limit]
@@ -872,9 +832,7 @@ class Post(models.Model):
community = models.ForeignKey(Community, null=True, on_delete=models.CASCADE) community = models.ForeignKey(Community, null=True, on_delete=models.CASCADE)
feeling = models.SmallIntegerField(default=0, choices=feelings) feeling = models.SmallIntegerField(default=0, choices=feelings)
body = models.TextField(null=True) body = models.TextField(null=True)
drawing = models.CharField(max_length=200, null=True, blank=True) file = models.FileField(max_length=1024, null=True, blank=True, upload_to='post_file/%Y/%m/%d/')
screenshot = models.CharField(max_length=1200, null=True, blank=True, default='')
video = models.CharField(max_length=256, null=True, blank=True, default='')
url = models.URLField(max_length=1200, null=True, blank=True, default='') url = models.URLField(max_length=1200, null=True, blank=True, default='')
spoils = models.BooleanField(default=False) spoils = models.BooleanField(default=False)
disable_yeah = models.BooleanField(default=False) disable_yeah = models.BooleanField(default=False)
@@ -898,8 +856,6 @@ class Post(models.Model):
def trun(self): def trun(self):
if self.is_rm: if self.is_rm:
return 'deleted' return 'deleted'
if self.drawing:
return 'drawing'
else: else:
return self.body return self.body
def yt_vid(self): def yt_vid(self):
@@ -917,6 +873,26 @@ class Post(models.Model):
return (self.creator == user) return (self.creator == user)
else: else:
return False return False
def file_type(self):
mimetypes.add_type("image/webp", ".webp")
# Get the file MIME type
if not self.file:
return None
mime_type, encoding = mimetypes.guess_type(self.file.name)
if mime_type:
# Determine the type of file
type_main, type_sub = mime_type.split('/')
if type_main == 'image':
return 1
elif type_main == 'video':
return 2
elif type_main == 'audio':
return 3
else:
return 0
else:
return 0
#def yeah_notification(self, request): #def yeah_notification(self, request):
# ???? What is this # ???? What is this
#Notification.give_notification #Notification.give_notification
@@ -1027,43 +1003,6 @@ class Post(models.Model):
for post in comments: for post in comments:
post.setup(request) post.setup(request)
return comments return comments
def create_comment(self, request):
if not self.can_comment(request):
return False
limit = request.user.limit_remaining()
if not limit is False and not limit > 0:
return 8
del(limit)
if self.is_mine(request.user) and Comment.real.filter(creator=request.user, created__gt=timezone.now() - timedelta(seconds=2)).exists():
return 3
elif not self.is_mine(request.user) and Comment.real.filter(creator=request.user, created__gt=timezone.now() - timedelta(seconds=10)).exists():
return 3
if not request.user.has_freedom() and (request.POST.get('url') or request.FILES.get('screen')):
return 6
for c in request.POST['body']:
if unicodedata.combining(c):
return 12
if not request.user.is_active:
return 6
if len(request.POST['body']) > 2200 or (len(request.POST['body']) < 1 and not request.POST.get('_post_type') == 'painting'):
return 1
if request.user.unread_warning():
return 13
upload = None
drawing = None
if request.FILES.get('screen'):
upload = util.image_upload(request.FILES['screen'], True)
if upload == 1:
return 2
if request.POST.get('_post_type') == 'painting':
if not request.POST.get('painting'):
return 2
drawing = util.image_upload(request.POST['painting'], False, True)
if drawing == 1:
return 2
new_post = self.comment_set.create(body=request.POST.get('body'), creator=request.user, community=self.community, original_post=self, feeling=int(request.POST.get('feeling_id', 0)), spoils=bool(request.POST.get('is_spoiler')), drawing=drawing, screenshot=upload)
new_post.is_mine = True
return new_post
def recent_comment(self): def recent_comment(self):
if self.number_comments() < 1: if self.number_comments() < 1:
return False return False
@@ -1118,12 +1057,6 @@ class Post(models.Model):
else: else:
self.status = 2 self.status = 2
AuditLog.objects.create(type=0, post=self, user=self.creator, by=request.user) AuditLog.objects.create(type=0, post=self, user=self.creator, by=request.user)
if self.screenshot:
util.image_rm(self.screenshot)
self.screenshot = None
if self.drawing:
util.image_rm(self.drawing)
self.drawing = None
self.save() self.save()
def setup(self, request): def setup(self, request):
self.has_yeah = self.has_yeah(request) self.has_yeah = self.has_yeah(request)
@@ -1143,8 +1076,7 @@ class Comment(models.Model):
community = models.ForeignKey(Community, on_delete=models.CASCADE) community = models.ForeignKey(Community, on_delete=models.CASCADE)
feeling = models.SmallIntegerField(default=0, choices=feelings) feeling = models.SmallIntegerField(default=0, choices=feelings)
body = models.TextField(null=True) body = models.TextField(null=True)
screenshot = models.CharField(max_length=1200, null=True, blank=True, default='') file = models.FileField(max_length=1024, null=True, blank=True, upload_to='comment_file/%Y/%m/%d/')
drawing = models.CharField(max_length=200, null=True, blank=True)
spoils = models.BooleanField(default=False) spoils = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
edited = models.DateTimeField(auto_now=True) edited = models.DateTimeField(auto_now=True)
@@ -1164,10 +1096,27 @@ class Comment(models.Model):
def trun(self): def trun(self):
if self.is_rm: if self.is_rm:
return 'deleted' return 'deleted'
if self.drawing:
return '(drawing)'
else: else:
return self.body return self.body
def file_type(self):
mimetypes.add_type("image/webp", ".webp")
# Get the file MIME type
if not self.file:
return None
mime_type, encoding = mimetypes.guess_type(self.file.name)
if mime_type:
# Determine the type of file
type_main, type_sub = mime_type.split('/')
if type_main == 'image':
return 1
elif type_main == 'video':
return 2
elif type_main == 'audio':
return 3
else:
return 0
else:
return 0
def is_mine(self, user): def is_mine(self, user):
if user.is_authenticated: if user.is_authenticated:
return (self.creator == user) return (self.creator == user)
@@ -1238,12 +1187,6 @@ class Comment(models.Model):
else: else:
self.status = 2 self.status = 2
AuditLog.objects.create(type=1, comment=self, user=self.creator, by=request.user) AuditLog.objects.create(type=1, comment=self, user=self.creator, by=request.user)
if self.screenshot:
util.image_rm(self.screenshot)
self.screenshot = None
if self.drawing:
util.image_rm(self.drawing)
self.drawing = None
self.save() self.save()
def setup(self, request): def setup(self, request):
self.has_yeah = self.has_yeah(request) self.has_yeah = self.has_yeah(request)
@@ -1277,23 +1220,19 @@ class Profile(models.Model):
origin_id = models.CharField(max_length=16, null=True, blank=True) origin_id = models.CharField(max_length=16, null=True, blank=True)
origin_info = models.CharField(max_length=255, null=True, blank=True) origin_info = models.CharField(max_length=255, null=True, blank=True)
comment = models.TextField(blank=True, default='') comment = models.TextField(blank=True, null=True)
country = models.CharField(max_length=120, blank=True, default='') country = models.CharField(max_length=120, blank=True)
whatareyou = models.CharField(max_length=120, blank=True, default='') whatareyou = models.CharField(max_length=120, blank=True)
#birthday = models.DateField(null=True, blank=True) #birthday = models.DateField(null=True, blank=True)
id_visibility = models.SmallIntegerField(default=0, choices=visibility) id_visibility = models.SmallIntegerField(default=0, choices=visibility)
pronoun_is = models.IntegerField(default=0, help_text='haha attack helicopter.', choices=( pronoun_is = models.CharField(max_length=16, blank=True)
(0, "Not specified"), (1, "He/him"), (2, "She/her"), (3, "He/she"), (4, "It"), (5, "They/Them")
))
let_friendrequest = models.SmallIntegerField(default=0, choices=visibility) let_friendrequest = models.SmallIntegerField(default=0, choices=visibility)
yeahs_visibility = models.SmallIntegerField(default=0, choices=visibility) yeahs_visibility = models.SmallIntegerField(default=0, choices=visibility)
comments_visibility = models.SmallIntegerField(default=2, choices=visibility) comments_visibility = models.SmallIntegerField(default=2, choices=visibility)
#relationship_visibility = models.SmallIntegerField(default=0, choices=visibility) weblink = models.CharField(max_length=1200, blank=True)
weblink = models.CharField(max_length=1200, blank=True, default='') external = models.CharField(max_length=255, blank=True)
#gameskill = models.SmallIntegerField(default=0)
external = models.CharField(max_length=255, blank=True, default='')
favorite = models.ForeignKey(Post, blank=True, null=True, on_delete=models.SET_NULL) favorite = models.ForeignKey(Post, blank=True, null=True, on_delete=models.SET_NULL)
let_yeahnotifs = models.BooleanField(default=True) let_yeahnotifs = models.BooleanField(default=True)
@@ -1302,9 +1241,7 @@ class Profile(models.Model):
#let_avatar = models.BooleanField(default=False) #let_avatar = models.BooleanField(default=False)
# Post limit, 0 for none # Post limit, 0 for none
limit_post = models.SmallIntegerField(default=0, help_text='Great for spammers, set to \"0\" to remove the restriction.') limit_post = models.SmallIntegerField(default=0, help_text='Great for spammers, set to \"0\" to remove the restriction.')
# If this is true, the user can't change their avatar or nickname
cannot_edit = models.BooleanField(default=False, help_text='Make it so this user cannot change settings.') cannot_edit = models.BooleanField(default=False, help_text='Make it so this user cannot change settings.')
email_login = models.SmallIntegerField(default=1, choices=((0, 'Do not allow'), (1, 'Okay'), (2, 'Only allow')))
def __str__(self): def __str__(self):
return "profile for " + self.user.username return "profile for " + self.user.username
@@ -1609,38 +1546,13 @@ class Conversation(models.Model):
for msg in msgs: for msg in msgs:
msg.mine = msg.mine(request.user) msg.mine = msg.mine(request.user)
return msgs return msgs
def make_message(self, request):
if not request.user.has_freedom() and (request.POST.get('url') or request.FILES.get('screen')):
return 6
if Message.real.filter(creator=request.user, created__gt=timezone.now() - timedelta(seconds=2)).exists():
return 3
if len(request.POST['body']) > 50000 or (len(request.POST['body']) < 1 and not request.POST.get('_post_type') == 'painting'):
return 1
if request.user.unread_warning():
return 4
upload = None
drawing = None
if request.FILES.get('screen'):
upload = util.image_upload(request.FILES['screen'], True)
if upload == 1:
return 2
if request.POST.get('_post_type') == 'painting':
if not request.POST.get('painting'):
return 2
drawing = util.image_upload(request.POST['painting'], False, True)
if drawing == 1:
return 2
new_post = self.message_set.create(body=request.POST.get('body'), creator=request.user, feeling=int(request.POST.get('feeling_id', 0)), drawing=drawing, screenshot=upload)
new_post.mine = True
return new_post
class Message(models.Model): class Message(models.Model):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE) conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE)
feeling = models.SmallIntegerField(default=0, choices=feelings) feeling = models.SmallIntegerField(default=0, choices=feelings)
body = models.TextField(null=True) body = models.TextField(null=True)
drawing = models.CharField(max_length=200, null=True, blank=True) file = models.FileField(max_length=1024, null=True, blank=True, upload_to='message_file/%Y/%m/%d/')
screenshot = models.CharField(max_length=1200, null=True, blank=True, default='') url = models.URLField(max_length=1200, null=True, blank=True)
url = models.URLField(max_length=1200, null=True, blank=True, default='')
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
read = models.BooleanField(default=False) read = models.BooleanField(default=False)
is_rm = models.BooleanField(default=False) is_rm = models.BooleanField(default=False)
@@ -1654,10 +1566,27 @@ class Message(models.Model):
def trun(self): def trun(self):
if self.is_rm: if self.is_rm:
return 'deleted' return 'deleted'
if self.drawing:
return '(drawing)'
else: else:
return self.body return self.body
def file_type(self):
mimetypes.add_type("image/webp", ".webp")
# Get the file MIME type
if not self.file:
return None
mime_type, encoding = mimetypes.guess_type(self.file.name)
if mime_type:
# Determine the type of file
type_main, type_sub = mime_type.split('/')
if type_main == 'image':
return 1
elif type_main == 'video':
return 2
elif type_main == 'audio':
return 3
else:
return 0
else:
return 0
def mine(self, user): def mine(self, user):
if self.creator == user: if self.creator == user:
return True return True
@@ -1665,14 +1594,7 @@ class Message(models.Model):
def rm(self, request): def rm(self, request):
if self.conversation.source == request.user or self.conversation.target == request.user: if self.conversation.source == request.user or self.conversation.target == request.user:
self.is_rm = True self.is_rm = True
if self.screenshot:
util.image_rm(self.screenshot)
self.screenshot = None
if self.drawing:
util.image_rm(self.drawing)
self.drawing = None
self.save() self.save()
def makeopt(ls): def makeopt(ls):
if len(ls) < 1: if len(ls) < 1:
raise ValueError raise ValueError
@@ -1843,12 +1765,10 @@ class ProfileHistory(models.Model):
# blah blah blah # blah blah blah
# this method will be executed when... # this method will be executed when...
def rm_post_image(sender, instance, **kwargs): '''def rm_post_image(sender, instance, **kwargs):
if instance.screenshot: if instance.file:
util.image_rm(instance.screenshot) util.image_rm(instance.file)
if instance.drawing:
util.image_rm(instance.drawing)
# when pre_delete happens on these # when pre_delete happens on these
pre_delete.connect(rm_post_image, sender=Post) pre_delete.connect(rm_post_image, sender=Post)
pre_delete.connect(rm_post_image, sender=Comment) pre_delete.connect(rm_post_image, sender=Comment)
pre_delete.connect(rm_post_image, sender=Message) pre_delete.connect(rm_post_image, sender=Message)'''
@@ -60,13 +60,13 @@
</div> </div>
{% endif %} {% endif %}
<div id="the-post"> <div id="the-post">
{% if comment.drawing %}
<p class="reply-content-memo"><img src="{{ comment.drawing }}" class="post-memo"></p>
{% else %}
<p class="reply-content-text">{{ comment.body|linebreaksbr }}</p> <p class="reply-content-text">{{ comment.body|linebreaksbr }}</p>
{% if comment.file %}
{% if comment.file_type == 1 %}<div class="screenshot-container still-image"><img src="{{ comment.file.url }}"></div>
{% elif comment.file_type == 2 %}<div class="screenshot-container video"><video controls src="{{ comment.file.url }}" style="max-width:100%;max-height: 450px;"></video></div>
{% elif comment.file_type == 3 %}<div class="screenshot-container audio"><audio controls src="{{ comment.file.url }}"></audio></div>
{% elif comment.file_type == 0 %}<a class="button download-confirm" href="{{ comment.file.url }}">Download {{ comment.file }}</a>
{% endif %} {% endif %}
{% if comment.screenshot %}
<div class="screenshot-container still-image"><img src="{{ comment.screenshot }}"></div>
{% endif %} {% endif %}
<div class="post-meta"> <div class="post-meta">
<button type="button"{% if not comment.can_yeah %} disabled{% endif %} class="symbol submit yeah-button <button type="button"{% if not comment.can_yeah %} disabled{% endif %} class="symbol submit yeah-button
@@ -11,14 +11,6 @@
{% feeling_selector %} {% feeling_selector %}
<div class="textarea-with-menu active-text"> <div class="textarea-with-menu active-text">
<menu class="textarea-menu">
<li><label class="textarea-menu-text">
<input type="radio" name="_post_type" value="body">
</label></li>
<li><label class="textarea-menu-memo">
<input type="radio" name="_post_type" value="painting">
</label></li>
</menu>
<div class="textarea-container"> <div class="textarea-container">
<textarea name="body" class="textarea-text textarea" maxlength="2200" placeholder="Add a comment here." data-required></textarea> <textarea name="body" class="textarea-text textarea" maxlength="2200" placeholder="Add a comment here." data-required></textarea>
</div> </div>
@@ -21,27 +21,23 @@
<span class="spoiler">Edited</span>· <span class="spoiler">Edited</span>·
{% endif %} {% endif %}
<a class="timestamp" {% if post.spoils and not post.is_mine %}data-href-hidden{% else %}href{% endif %}="{% if post.is_reply %}{% url "main:comment-view" post.id %}{% else %}{% url "main:post-view" post.id %}{% endif %}">{% time post.created %}</a> <a class="timestamp" {% if post.spoils and not post.is_mine %}data-href-hidden{% else %}href{% endif %}="{% if post.is_reply %}{% url "main:comment-view" post.id %}{% else %}{% url "main:post-view" post.id %}{% endif %}">{% time post.created %}</a>
{% if post.drawing %}
<span class="spoiler">(handwritten)</span>
{% endif %}
</p> </p>
<div class="body post-content"> <div class="body post-content">
{% if post.yt_vid %} {% if post.yt_vid and not post.file_type == 2 %}
<a href="{% url "main:post-view" post.id %}" class="screenshot-container video"><img height="48" src="https://i.ytimg.com/vi/{{ post.yt_vid }}/default.jpg"></a> <a href="{% url "main:post-view" post.id %}" class="screenshot-container video"><img height="48" src="https://i.ytimg.com/vi/{{ post.yt_vid }}/default.jpg"></a>
{% elif post.discord_vid %}
<div class="screenshot-container video"><video src="{{ post.url }}" height="48"></video></div>
{% endif %} {% endif %}
{% if post.drawing %}
<p class="post-content-memo"><img src="{{ post.drawing }}" class="post-memo"></p>
{% else %}
{% if post.has_line_trun %} {% if post.has_line_trun %}
<p class="post-content-text">{{ post.body|truncatechars:200 }}</p> <p class="post-content-text">{{ post.body|truncatechars:200 }}</p>
{% else %} {% else %}
<p class="post-content-text">{{ post.body|truncatechars:200|linebreaksbr }}</p> <p class="post-content-text">{{ post.body|truncatechars:200|linebreaksbr }}</p>
{% endif %} {% endif %}
{% if post.file %}
{% if post.file_type == 1 %}<a class="screenshot-container still-image" href="{% if post.is_reply %}{% url "main:comment-view" post.id %}{% else %}{% url "main:post-view" post.id %}{% endif %}"><img src="{{ post.file.url }}"></a>
{% elif post.file_type == 2 %}<div class="screenshot-container still-image"><video controls src="{{ post.file.url }}" style="max-width:100%;max-height: 450px;"></video></div>
{% elif post.file_type == 3 %}<div class="screenshot-container audio"><audio controls src="{{ post.file.url }}"></audio></div>
{% else %}<a>File attached</a>
{% endif %}
{% endif %} {% endif %}
{% if post.screenshot %}<a class="screenshot-container still-image" href="{% if post.is_reply %}{% url "main:comment-view" post.id %}{% else %}{% url "main:post-view" post.id %}{% endif %}"><img src="{{ post.screenshot }}"></a>{% endif %}
{% if post.video %}<div class="screenshot-container still-image"><video controls src="{{ post.video }}" style="max-width:100%;max-height: 450px;"></video></div>{% endif %}
{% if post.spoils and not post.is_mine and not post.creator.banned %} {% if post.spoils and not post.is_mine and not post.creator.banned %}
<div class="hidden-content"><p>This post contains spoilers.</p> <div class="hidden-content"><p>This post contains spoilers.</p>
<button type="button" class="hidden-content-button">View Post</button> <button type="button" class="hidden-content-button">View Post</button>
@@ -1,13 +1,7 @@
<!-- <div class="file-button-container">
<span class="input-div">Image <span>PNG, JPG, and GIF are allowed.</span></span>
<input type="file" class="file-button" accept="image/png, image/jpeg, image/jpg, image/svg+xml, image/bmp, image/gif">
</div>
-->
<label class="file-button-container"> <label class="file-button-container">
<span class="input-label">Image <span id="image-dimensions">PNG and JPEG are allowed.</span></span> <span class="input-label">File <span id="image-dimensions">Images, videos and audio are supported.</span></span>
<span class="button file-upload-button">Upload</span> <span class="button file-upload-button">Upload</span>
<input accept="image/gif, image/png, image/jpeg, image/jpg, image/svg+xml, image/webp, image/bmp" type="file" style="display: none;" id="upload-file" name="screen"> <input type="file" style="display: none;" id="upload-file" name="file">
<div id="upload-preview-container" class="screenshot-container still-image" style="display: none;"> <div id="upload-preview-container" class="screenshot-container still-image" style="display: none;">
<img id="upload-preview"> <img id="upload-preview">
<!-- not functional currently <!-- not functional currently
@@ -15,8 +9,4 @@
--> -->
</div> </div>
</label> </label>
<div class="file-button-container">
<span class="input-label">Video <span>MP4s and WEBMs are allowed.</span></span>
<input type="file" class="file-button" name="video" accept="video/mp4, video/webm">
</div>
@@ -3,14 +3,6 @@
{% csrf_token %} {% csrf_token %}
{% feeling_selector %} {% feeling_selector %}
<div class="textarea-with-menu active-text"> <div class="textarea-with-menu active-text">
<menu class="textarea-menu">
<li><label class="textarea-menu-text">
<input type="radio" name="_post_type" value="body">
</label></li>
<li><label class="textarea-menu-memo">
<input type="radio" name="_post_type" value="painting">
</label></li>
</menu>
<div class="textarea-container"> <div class="textarea-container">
<textarea name="body" class="textarea-text textarea " maxlength="12000" placeholder="Write a message here." data-open-folded-form="" data-required=""></textarea> <textarea name="body" class="textarea-text textarea " maxlength="12000" placeholder="Write a message here." data-open-folded-form="" data-required=""></textarea>
</div> </div>
@@ -10,8 +10,12 @@
{% else %} {% else %}
<div class="post-content-text">{{ message.body }}</div> <div class="post-content-text">{{ message.body }}</div>
{% endif %} {% endif %}
{% if message.screenshot %} {% if message.file %}
<div class="screenshot-container still-image"><img src="{{ message.screenshot }}"></div> {% if message.file_type == 1 %}<div class="screenshot-container still-image"><img src="{{ message.file.url }}"></div>
{% elif message.file_type == 2 %}<div class="screenshot-container"><video controls src="{{ message.file.url }}" style="max-width:100%;max-height: 450px;"></video></div>
{% elif message.file_type == 3 %}<div class="screenshot-container"><audio controls src="{{ message.file.url }}"></audio></div>
{% elif message.file_type == 0 %}<a class="button download-confirm" href="{{ message.file.url }}">Download {{ message.file }}</a>
{% endif %}
{% endif %} {% endif %}
</div> </div>
</div> </div>
@@ -5,9 +5,6 @@
<p class="user-name"><a {% if comment.creator.color %}style=color:{{ comment.creator.color }}{% endif %} href="{% url "main:user-view" comment.creator.username %}">{{ comment.creator.nickname }}</a></p> <p class="user-name"><a {% if comment.creator.color %}style=color:{{ comment.creator.color }}{% endif %} href="{% url "main:user-view" comment.creator.username %}">{{ comment.creator.nickname }}</a></p>
<p class="timestamp-container"> <p class="timestamp-container">
<a class="timestamp" {% if comment.spoils and not comment.is_mine %}data-href-hidden{% else %}href{% endif %}="{% url "main:comment-view" comment.id %}">{% time comment.created %}</a> <a class="timestamp" {% if comment.spoils and not comment.is_mine %}data-href-hidden{% else %}href{% endif %}="{% url "main:comment-view" comment.id %}">{% time comment.created %}</a>
{% if comment.drawing %}
<span class="spoiler">(handwritten)</span>
{% endif %}
{% if comment.has_edit %} {% if comment.has_edit %}
<span class="spoiler">· Edited</span> <span class="spoiler">· Edited</span>
{% endif %} {% endif %}
@@ -15,13 +12,13 @@
</p> </p>
</div> </div>
{% if comment.drawing %}
<p class="reply-content-memo"><img src="{{ comment.drawing }}"></p>
{% else %}
<div class="reply-content-text">{{ comment.body|linebreaksbr }}</div> <div class="reply-content-text">{{ comment.body|linebreaksbr }}</div>
{% if comment.file %}
{% if comment.file_type == 1 %}<div class="screenshot-container still-image"><img src="{{ comment.file.url }}"></div>
{% elif comment.file_type == 2 %}<div class="screenshot-container"><video controls src="{{ comment.file.url }}" style="max-width:100%;max-height: 450px;"></video></div>
{% elif comment.file_type == 3 %}<div class="screenshot-container"><audio controls src="{{ comment.file.url }}"></audio></div>
{% else %}<a>File attached</a>
{% endif %} {% endif %}
{% if comment.screenshot %}
<div class="screenshot-container still-image"><img src="{{ comment.screenshot }}"></div>
{% endif %} {% endif %}
{% if comment.spoils and not comment.is_mine and not comment.creator.banned %} {% if comment.spoils and not comment.is_mine and not comment.creator.banned %}
<div class="hidden-content"><p>This comment contains spoilers.</p> <div class="hidden-content"><p>This comment contains spoilers.</p>
@@ -10,19 +10,6 @@
{% feeling_selector %} {% feeling_selector %}
<div class="textarea-with-menu active-text"> <div class="textarea-with-menu active-text">
<menu class="textarea-menu">
<li><label class="textarea-menu-text">
<input type="radio" name="_post_type" value="body">
</label></li>
<li><label class="textarea-menu-memo">
<input type="radio" name="_post_type" value="painting">
</label></li>
<!--
<li><label class="textarea-menu-poll">
<input type="radio" name="_post_type" value="poll">
</label></li>
-->
</menu>
<div class="textarea-container"> <div class="textarea-container">
<textarea name="body" class="textarea-text textarea " maxlength="2200" placeholder="{% if community.is_activity %}Share a post to your followers.{% else %}Share your thoughts in a post to {{ community.name }}{% endif %}" data-open-folded-form="" data-required=""></textarea> <textarea name="body" class="textarea-text textarea " maxlength="2200" placeholder="{% if community.is_activity %}Share a post to your followers.{% else %}Share your thoughts in a post to {{ community.name }}{% endif %}" data-open-folded-form="" data-required=""></textarea>
</div> </div>
@@ -1,4 +1,4 @@
{% load closedverse_tags %}<div {% if post.spoils and not post.is_mine or post.creator.banned %}data-href-hidden{% else %}data-href{% endif %}="{% url "main:post-view" post.id %}" class="post trigger{% if post.screenshot and not for_announcements %} with-image{% endif %}{% if post.spoils and not post.is_mine or post.creator.banned %} hidden test-hidden{% endif %}" tabindex="0"> {% load closedverse_tags %}<div {% if post.spoils and not post.is_mine or post.creator.banned %}data-href-hidden{% else %}data-href{% endif %}="{% url "main:post-view" post.id %}" class="post trigger{% if post.file_type == 1 and not for_announcements %} with-image{% endif %}{% if post.spoils and not post.is_mine or post.creator.banned %} hidden test-hidden{% endif %}" tabindex="0">
<p class="community-container"><a {% if post.community.clickable %}href="{% url "main:community-view" post.community_id %}"{% endif %}><img src="{{ post.community.icon }}" class="community-icon">{{ post.community.name }}</a></p> <p class="community-container"><a {% if post.community.clickable %}href="{% url "main:community-view" post.community_id %}"{% endif %}><img src="{{ post.community.icon }}" class="community-icon">{{ post.community.name }}</a></p>
@@ -16,28 +16,23 @@
<span class="spoiler">Edited ({% time post.edited %})</span>· <span class="spoiler">Edited ({% time post.edited %})</span>·
{% endif %} {% endif %}
<a class="timestamp" {% if post.spoils and not post.is_mine %}data-href-hidden{% else %}href{% endif %}="{% url "main:post-view" post.id %}">{% time post.created %}</a> <a class="timestamp" {% if post.spoils and not post.is_mine %}data-href-hidden{% else %}href{% endif %}="{% url "main:post-view" post.id %}">{% time post.created %}</a>
{% if post.drawing %}
<span class="spoiler">(handwritten)</span>
{% endif %}
</p> </p>
{% if post.yt_vid %} {% if post.yt_vid and not post.file_type == 2 %}
<a href="{% url "main:post-view" post.id %}" class="{% if not for_announcements %}screenshot-container {% else %}announcement-container {% endif %}video"><img height="48" src="https://i.ytimg.com/vi/{{ post.yt_vid }}/default.jpg"></a> <a href="{% url "main:post-view" post.id %}" class="{% if not for_announcements %}screenshot-container {% else %}announcement-container {% endif %}video"><img height="48" src="https://i.ytimg.com/vi/{{ post.yt_vid }}/default.jpg"></a>
{% endif %} {% endif %}
{% if post.screenshot %} {% if post.file %}
<a href="{% url "main:post-view" post.id %}" class="{% if not for_announcements %}screenshot-container {% else %}announcement-container {% endif %}still-image"><img src="{{ post.screenshot }}"></a> {% if post.file_type == 1 %}
<a href="{% url "main:post-view" post.id %}" class="{% if not for_announcements %}screenshot-container {% else %}announcement-container {% endif %}still-image"><img src="{{ post.file.url }}"></a>
{% elif post.file_type == 2 %}
<div class="{% if not for_announcements %}screenshot-container {% else %}announcement-container {% endif %} video"><video src="{{ post.file.url }}" width="50" height="48"></video></div>
{% else %}<a>File attached</a>
{% endif %} {% endif %}
{% if post.video %}
<div class="{% if not for_announcements %}screenshot-container {% else %}announcement-container {% endif %} video"><video src="{{ post.video }}" width="50" height="48"></video></div>
{% endif %} {% endif %}
{% if post.drawing %}
<p class="post-content-memo"><img src="{{ post.drawing }}" class="post-memo"></p>
{% else %}
{% if post.has_line_trun %} {% if post.has_line_trun %}
<p class="post-content-text">{{ post.body|truncatechars:100 }}</p> <p class="post-content-text">{{ post.body|truncatechars:100 }}</p>
{% else %} {% else %}
<p class="post-content-text">{{ post.body|truncatechars:100|linebreaksbr }}</p> <p class="post-content-text">{{ post.body|truncatechars:100|linebreaksbr }}</p>
{% endif %} {% endif %}
{% endif %}
{% if post.spoils and not post.is_mine and not post.creator.banned %} {% if post.spoils and not post.is_mine and not post.creator.banned %}
<div class="hidden-content"><p>This post contains spoilers. <div class="hidden-content"><p>This post contains spoilers.
<button type="button" class="hidden-content-button">View Post</button> <button type="button" class="hidden-content-button">View Post</button>
@@ -23,9 +23,9 @@
{% endif %} {% endif %}
<div class="sidebar-container"> <div class="sidebar-container">
{% if profile.favorite.screenshot %} {% if profile.favorite.file %}
<a href="{% url "main:post-view" profile.favorite_id %}" id="sidebar-cover"> <a href="{% url "main:post-view" profile.favorite_id %}" id="sidebar-cover">
<img src="{{ profile.favorite.screenshot }}" class="sidebar-cover-image"> <img src="{{ profile.favorite.file.url }}" class="sidebar-cover-image">
</a> </a>
{% endif %} {% endif %}
{% user_sidebar_info user profile %} {% user_sidebar_info user profile %}
@@ -150,12 +150,14 @@
</div> </div>
{% endif %} {% endif %}
<div class="user-data"> <div class="user-data">
{% if profile.country %}
<div class="data-content"> <div class="data-content">
<h4><span>Region</span></h4> <h4><span>Region</span></h4>
<div class="note"> <div class="note">
<span>{% if profile.country %}{{ profile.country }}{% else %}Not Set{% endif %}</span> <span>{{ profile.country }}</span>
</div> </div>
</div> </div>
{% endif %}
<div class="data-content"> <div class="data-content">
<h4><span>NNID</span></h4> <h4><span>NNID</span></h4>
<div class="note"> <div class="note">
@@ -192,9 +194,9 @@
{% endif %} {% endif %}
{% if profile.pronoun_is %} {% if profile.pronoun_is %}
<div class="data-content"> <div class="data-content">
<h4><span>Preferred Pronoun</span></h4> <h4><span>Pronouns</span></h4>
<div class="note"> <div class="note">
<span>{{ profile.get_pronoun_is_display }}</span> <span>{{ profile.pronoun_is }}</span>
</div> </div>
</div> </div>
{% endif %} {% endif %}
@@ -214,12 +216,14 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if profile.weblink %}
<div class="favorite-game-genre"> <div class="favorite-game-genre">
<h4><span>Web URL</span></h4> <h4><span>Web URL</span></h4>
<div class="note"> <div class="note">
<span>{% if profile.weblink %}<a class="link-confirm" href="{% if not profile.got_fullurl %}http://{% endif %}{{ profile.weblink }}">{{ profile.weblink }}</a>{% else %}Not Set{% endif %}</span> <span><a class="link-confirm" href="{% if not profile.got_fullurl %}http://{% endif %}{{ profile.weblink }}">{{ profile.weblink }}</a></span>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
</div> </div>
{% if user.community_favorites %} {% if user.community_favorites %}
@@ -32,7 +32,7 @@
</p> </p>
{% if friend.get_latest_msg %} {% if friend.get_latest_msg %}
<span class="timestamp">{% time friend.get_latest_msg.created %}</span> <span class="timestamp">{% time friend.get_latest_msg.created %}</span>
<p class="text {% if friend.get_latest_msg.drawing or friend.get_latest_msg.is_rm %}type-memo {% endif %}{% if friend.get_latest_msg.mine %}my{% else %}other{% endif %}">{{ friend.get_latest_msg.trun }}</p> <p class="text {% if friend.get_latest_msg.is_rm %}type-memo {% endif %}{% if friend.get_latest_msg.mine %}my{% else %}other{% endif %}">{{ friend.get_latest_msg.trun }}</p>
{% else %} {% else %}
<p class="text placeholder">You haven't exchanged messages with this user yet.</p> <p class="text placeholder">You haven't exchanged messages with this user yet.</p>
{% endif %} {% endif %}
@@ -16,7 +16,7 @@
{% if post.is_mine and not post.has_edit %} {% if post.is_mine and not post.has_edit %}
<button type="button" class="symbol button edit-button edit-post-button"><span class="symbol-label">Edit</span></button> <button type="button" class="symbol button edit-button edit-post-button"><span class="symbol-label">Edit</span></button>
{% endif %} {% endif %}
{% if post.is_mine and post.screenshot %} {% if post.is_mine and post.file_type == 1 %}
<button type="button" class="symbol button edit-button profile-post-button{% if post.is_favorite %} done {% endif %}" data-action="{% if post.is_favorite %}{% url "main:post-unset-profile" post.id %}{% else %}{% url "main:post-set-profile" post.id %}{% endif %}"><span class="symbol-label">Set as profile post</span></button> <button type="button" class="symbol button edit-button profile-post-button{% if post.is_favorite %} done {% endif %}" data-action="{% if post.is_favorite %}{% url "main:post-unset-profile" post.id %}{% else %}{% url "main:post-set-profile" post.id %}{% endif %}"><span class="symbol-label">Set as profile post</span></button>
{% endif %} {% endif %}
{% if post.can_lock_comments %} {% if post.can_lock_comments %}
@@ -33,9 +33,6 @@
{% p_username post.creator %} {% p_username post.creator %}
<p class="timestamp-container"> <p class="timestamp-container">
<span class="timestamp">{% time post.created %}</span> <span class="timestamp">{% time post.created %}</span>
{% if post.drawing %}
<span class="spoiler">(drawing)</span>
{% endif %}
<span class="spoiler-status{% if post.spoils %} spoiler{% endif %}">·Spoilers</span> <span class="spoiler-status{% if post.spoils %} spoiler{% endif %}">·Spoilers</span>
{% if post.has_edit %} {% if post.has_edit %}
· <span class="spoiler">Edited ({% time post.edited %})</span> · <span class="spoiler">Edited ({% time post.edited %})</span>
@@ -62,30 +59,17 @@
</div> </div>
{% endif %} {% endif %}
<div id="the-post"> <div id="the-post">
{% if post.drawing %}
<p class="post-content-memo"><img src="{{ post.drawing }}" class="post-memo"></p>
{% else %}
<p class="post-content-text">{{ post.body }}</p> <p class="post-content-text">{{ post.body }}</p>
{% if post.file %}
{% if post.file_type == 1 %}<div class="screenshot-container still-image"><img src="{{ post.file.url }}"></div>
{% elif post.file_type == 2 %}<div class="screenshot-container video"><video controls src="{{ post.file.url }}" style="max-width:100%;max-height: 450px;"></video></div>
{% elif post.file_type == 3 %}<div class="screenshot-container audio"><audio controls src="{{ post.file.url }}"></audio></div>
{% elif post.file_type == 0 %}<a class="button download-confirm" href="{{ post.file.url }}">Download {{ post.file }}</a>
{% endif %}
{% endif %} {% endif %}
{% if post.poll %}
<div class="post-poll{% if post.poll.has_vote %} selected{% endif %}" data-action="{% url "main:poll-vote" post.poll.id %}" data-action-unvote="{% url "main:poll-unvote" post.poll.id %}">
<a class="poll-votes">{{ post.poll.num_votes }} vote{% if not post.poll.num_votes == 1 %}s{% endif %}</a>
<div class="poll-options{% if post.screenshot %} with-background" style="background-image:url('{{ post.screenshot }}')"
<div class="poll-background" style="width:50%"></div>{% else %}">{% endif %}
{% for choice in post.poll.choices %}
<a class="poll-option{% if post.poll.has_vote and post.poll.has_vote.1 == choice %} selected{% endif %}" votes="{{ choice.num_votes }}">
{{ choice }}<span class="percentage">%</span>
</a>
{% endfor %}
</div>
</div>
{% elif post.screenshot %}<div class="screenshot-container still-image"><img src="{{ post.screenshot }}"></div>{% endif %}
{% if post.yt_vid %} {% if post.yt_vid %}
<div class="screenshot-container video"><iframe class="youtube-player" type="text/html" style="max-width:100%;max-height: 450px;" src="https://www.youtube.com/embed/{{ post.yt_vid }}?rel=0&modestbranding=1&iv_load_policy=3" frameborder="0"></iframe></div> <div class="screenshot-container video"><iframe class="youtube-player" type="text/html" style="max-width:100%;max-height: 450px;" src="https://www.youtube.com/embed/{{ post.yt_vid }}?rel=0&modestbranding=1&iv_load_policy=3" frameborder="0"></iframe></div>
{% elif post.video %}
<div class="screenshot-container video"><video controls src="{{ post.video }}" style="max-width:100%;max-height: 450px;"></video></div>
{% elif post.discord_vid %} {% elif post.discord_vid %}
<div class="DiscordCDN-container video"> <div class="DiscordCDN-container video">
<video class="discord-player" src="{{ post.url }}" style="max-width:100%;max-height: 450px;" controls></video> <video class="discord-player" src="{{ post.url }}" style="max-width:100%;max-height: 450px;" controls></video>
@@ -7,11 +7,6 @@
<form class="setting-form" method="post" action="{% url "main:user-view" user.username %}"> <form class="setting-form" method="post" action="{% url "main:user-view" user.username %}">
<ul class="settings-list"> <ul class="settings-list">
{% if profile.cannot_edit %}
<p>You cannot change user settings for this account</p>
{% endif %}
{% if not profile.cannot_edit %}
<li class="setting-nickname"> <li class="setting-nickname">
<p class="settings-label">Nickname</p> <p class="settings-label">Nickname</p>
<div class="center center-input"> <div class="center center-input">
@@ -25,20 +20,13 @@
<p class="note">Anything you write here will appear on your profile. Remember to keep it brief. Please don't write anything that'll violate <a href="{% url "main:help-rules" %}">{{ brand_name }}'s rules</a>.</p> <p class="note">Anything you write here will appear on your profile. Remember to keep it brief. Please don't write anything that'll violate <a href="{% url "main:help-rules" %}">{{ brand_name }}'s rules</a>.</p>
</li> </li>
<!-- <li class="setting-pronoun">
<li> <p class="settings-label">What are your pronouns?</p>
<p class="settings-label"><label for="relationship_visibility">Who should be able to see your friends, followers and following lists?</label></p> <div class="center center-input">
<div class="select-content"> <input type="text" name="pronouns" maxlength="16" placeholder="Pronouns" value="{{ profile.pronoun_is }}">
<div class="select-button">
<select name="relationship_visibility" id="relationship_visibility">
<option value="0"{% if profile.relationship_visibility == 0 %} selected{% endif %}>Everyone</option>
<option value="1"{% if profile.relationship_visibility == 1 %} selected{% endif %}>My friends</option>
<option value="2"{% if profile.relationship_visibility == 2 %} selected{% endif %}>Only me</option>
</select>
</div>
</div> </div>
<p class="note">Enter your pronouns here. </p>
</li> </li>
-->
<li class="setting-country"> <li class="setting-country">
<p class="settings-label">Region</p> <p class="settings-label">Region</p>
<div class="center center-input"> <div class="center center-input">
@@ -55,18 +43,6 @@
<input type="text" name="email" maxlength="255" placeholder="Email address" value="{{ user.email|default_if_none:"" }}"> <input type="text" name="email" maxlength="255" placeholder="Email address" value="{{ user.email|default_if_none:"" }}">
</div> </div>
<p class="note">Please note that your email can be a fake one, however if you need to reset your password, this must be accessible. You can't share emails.</p> <p class="note">Please note that your email can be a fake one, however if you need to reset your password, this must be accessible. You can't share emails.</p>
{% if user.email %}
<p class="settings-label"><label for="email_login">Allow logging in to your account with your e-mail address?</label></p>
<div class="select-content">
<div class="select-button">
<select name="email_login" id="email_login">
<option value="0"{% if profile.email_login == 0 %} selected{% endif %}>Do not allow (username login only)</option>
<option value="1"{% if profile.email_login == 1 %} selected{% endif %}>Allow</option>
<option value="2"{% if profile.email_login == 2 %} selected{% endif %}>Only allow (email login only)</option>
</select>
</div>
</div>
{% endif %}
</li> </li>
<li class="setting-website"> <li class="setting-website">
<p class="settings-label">Web URL</p> <p class="settings-label">Web URL</p>
@@ -98,9 +74,8 @@
<p class="note">This is the color your nickname will appear as. Set it to white (#ffffff) and it will return to the default. It will appear like so.</p> <p class="note">This is the color your nickname will appear as. Set it to white (#ffffff) and it will return to the default. It will appear like so.</p>
{% user_sidebar_info user %} {% user_sidebar_info user %}
</li> </li>
{% if profile.origin_id %}
<li> <li>
<p class="settings-label"><label for="id_visibility">Who should be able to see your Nintendo Network ID? ({{ profile.origin_id }})</label></p> <p class="settings-label"><label for="id_visibility">{% if profile.origin_id %}Who should be able to see your Nintendo Network ID? ({{ profile.origin_id }}){% else %}You have not entered a Nintendo Network ID yet, If you do, who should be able to see it?{% endif %}</label></p>
<div class="select-content"> <div class="select-content">
<div class="select-button"> <div class="select-button">
<select name="id_visibility" id="id_visibility"> <select name="id_visibility" id="id_visibility">
@@ -111,7 +86,6 @@
</div> </div>
</div> </div>
</li> </li>
{% endif %}
<li> <li>
<p class="settings-label"><label for="let_friendrequest">Who should be able to send you friend requests?</label></p> <p class="settings-label"><label for="let_friendrequest">Who should be able to send you friend requests?</label></p>
<div class="select-content"> <div class="select-content">
@@ -124,21 +98,6 @@
</div> </div>
</div> </div>
</li> </li>
<li>
<p class="settings-label"><label for="pronoun_dot_is">What's your preferred pronoun?</label></p>
<div class="select-content">
<div class="select-button">
<select name="pronoun_dot_is" id="pronoun_dot_is">
<option value="0"{% if profile.pronoun_is == 0 %} selected{% endif %}>I don't know</option>
<option value="1"{% if profile.pronoun_is == 1 %} selected{% endif %}>He/him</option>
<option value="2"{% if profile.pronoun_is == 2 %} selected{% endif %}>She/her</option>
<option value="3"{% if profile.pronoun_is == 3 %} selected{% endif %}>He/she</option>
<option value="4"{% if profile.pronoun_is == 4 %} selected{% endif %}>It</option>
<option value="5"{% if profile.pronoun_is == 5 %} selected{% endif %}>They/Them</option>
</select>
</div>
</div>
</li>
<li> <li>
<p class="settings-label"><label for="yeahs_visibility">Who should be able to see your Yeahs given?</label></p> <p class="settings-label"><label for="yeahs_visibility">Who should be able to see your Yeahs given?</label></p>
<div class="select-content"> <div class="select-content">
@@ -165,16 +124,12 @@
</li> </li>
<li class="setting-background"> <li class="setting-background">
<p class="settings-label">Website theme:</p> <p class="settings-label">Website theme:</p>
<div class="center center-input">
<input type="text" name="bg_url" maxlength="300" placeholder="Background URL" value="{% if user.bg_url %}{{ user.bg_url }}{% endif %}">
</div>
<p class="note">To set an image as the background for this site, paste its URL into the box above. Note: this change will only be visible to you.</p>
<div class="center center-input"> <div class="center center-input">
<input type="hidden" name="theme" maxlength="7" placeholder="Enter a hex color value here" value="{{ user.theme }}"> <input type="hidden" name="theme" maxlength="7" placeholder="Enter a hex color value here" value="{{ user.theme }}">
<button class="button color-thing2">Open color picker</button> <button class="button color-thing2">Open color picker</button>
</div> </div>
<p class="current-theme" style="color:{{ user.theme }}">Remember to save and refresh the page.</p> <p class="current-theme" style="color:{{ user.theme }}">Remember to save and refresh the page.</p>
{% if user.theme %}<input type="checkbox" name="reset-theme">Reset to default{% endif %} {% if user.theme %}<input type="checkbox" name="reset_theme">Reset to default{% endif %}
{% if settings.site_wide_theme_hex %}<p class="note">Default theme: "{{ settings.site_wide_theme_hex }}"</p>{% endif %} {% if settings.site_wide_theme_hex %}<p class="note">Default theme: "{{ settings.site_wide_theme_hex }}"</p>{% endif %}
</li> </li>
<li class="setting-background"> <li class="setting-background">
@@ -195,18 +150,18 @@
</li> </li>
<li class="setting-avatar"> <li class="setting-avatar">
<div class="icon-container"> <div class="icon-container">
<img class="icon nnid-icon mii{% if not user.has_mh %} none{% endif %}" src="{% miionly user.mh %}"> <img class="icon nnid-icon mii{% if not user.avatar_type == 2 %} none{% endif %}" src="{% miionly user.mh %}">
<img class="icon nnid-icon gravatar{% if user.has_mh or user.has_plain_avatar %} none{% endif %}" src="{{ user.gravatar }}"> <img class="icon nnid-icon gravatar{% if not user.avatar_type == 1 %} none{% endif %}" src="{{ user.gravatar }}">
{% if user.has_freedom %} {% if user.has_freedom %}
<img class="icon nnid-icon custom{% if not user.has_plain_avatar %} none{% endif %}" src="{{ user.do_avatar }}"> <img class="icon nnid-icon custom{% if not user.avatar_type == 0 %} none{% endif %}" src="{{ user.do_avatar }}">
{% endif %} {% endif %}
</div> </div>
<p class="settings-label">Do you want the avatar shown beside your content to use the Mii from your Nintendo Network ID or a Gravatar or a custom avatar the way God intended?</p> <p class="settings-label">Do you want the avatar shown beside your content to use the Mii from your Nintendo Network ID or a Gravatar or a custom avatar the way God intended?</p>
<label><input type="radio" name="avatar" value="0"{% if user.has_mh %} checked{% endif %}>Mii</label> <label><input type="radio" name="avatar" value="0"{% if user.avatar_type == 2 %} checked{% endif %}>Mii</label>
<label><input type="radio" name="avatar" value="1"{% if not user.has_mh %} checked{% endif %}>Gravatar</label> <label><input type="radio" name="avatar" value="1">Use Gravatar</label>
{% if user.has_freedom %} {% if user.has_freedom %}
<label><input type="radio" name="avatar" value="2"{% if user.has_plain_avatar%} checked{% endif %}>Custom</label> <label><input type="radio" name="avatar" value="2"{% if user.avatar_type == 0 %} checked{% endif %}>Custom</label>
<span id="upload-thing"{% if not user.has_plain_avatar %} class="none"{% endif %}>{% file_button %}</span> <span id="upload-thing"{% if not user.avatar_type == 0 %} class="none"{% endif %}>{% file_button %}</span>
<style> <style>
.setting-avatar div.file-button-container { display: none; } .setting-avatar div.file-button-container { display: none; }
</style> </style>
@@ -220,7 +175,6 @@
<div class="form-buttons"> <div class="form-buttons">
<input type="submit" class="black-button apply-button" value="Save these settings"> <input type="submit" class="black-button apply-button" value="Save these settings">
</div> </div>
{% endif %}
</ul></form></div></div> </ul></form></div></div>
{% endblock %} {% endblock %}
-134
View File
@@ -78,140 +78,6 @@ def recaptcha_verify(request, key):
return False return False
return True return True
def video_upload(video):
hash = sha1()
for chunk in video.chunks():
hash.update(chunk)
imhash = hash.hexdigest()
# only either webm or mp4
extension = video.name[-4:]
if extension == '.mp4':
fname = imhash + '.mp4'
elif extension == 'webm':
fname = imhash + '.webm'
else:
return 1
# simply only write image if the hashed path doesn't already exist
if not os.path.exists(settings.MEDIA_ROOT + fname):
with open(settings.MEDIA_ROOT + fname, "wb+") as destination:
for chunk in video.chunks():
destination.write(chunk)
return settings.MEDIA_URL + fname
ImageFile.LOAD_TRUNCATED_IMAGES = True
def image_upload(img, stream=False, drawing=False, avatar=False, icon=False, banner=False):
Imagefield = False
if banner or icon:
Imagefield = True
if stream:
decodedimg = img.read()
else:
try:
decodedimg = base64.b64decode(img)
except ValueError:
return 1
if stream:
if not 'image' in img.content_type:
return 1
# upload svg?
#if 'svg' in mime:
#
try:
im = Image.open(io.BytesIO(decodedimg))
# OSError is probably from invalid images, SyntaxError probably from unsupported images
except (OSError, SyntaxError):
return 1
# Taken from https://coderwall.com/p/nax6gg/fix-jpeg-s-unexpectedly-rotating-when-saved-with-pil
if hasattr(im, '_getexif'):
orientation = 0x0112
exif = im._getexif()
if exif is not None:
orientation = exif.get(orientation)
rotations = {
3: Image.ROTATE_180,
6: Image.ROTATE_270,
8: Image.ROTATE_90
}
if orientation in rotations:
im = im.transpose(rotations[orientation])
if avatar or icon:
tiny = True
# crop 1:1
width, height = im.size
min_dimension = min(width, height)
crop_size = min_dimension
left = (width - crop_size) // 2
top = (height - crop_size) // 2
right = left + crop_size
bottom = top + crop_size
im = im.crop((left, top, right, bottom))
im.thumbnail((256, 256))
else:
tiny = False
im.thumbnail((1280, 1280))
# Let's check the aspect ratio and see if it's crazy
# IF this is a drawing
if drawing and ((im.size[0] / im.size[1]) < 0.30):
return 1
# I know some people have aneurysms when they see people actually using SHA1 in the real world, for anything in general.
# Yes, we are really using it. Sorry if that offends you. It's just fast and I don't feel I need anything more random, since we are talking about IMAGES.
if stream:
hash = sha1()
for chunk in img.chunks():
hash.update(chunk)
imhash = hash.hexdigest()
else:
imhash = sha1(im.tobytes()).hexdigest()
# File saving target
target = 'png'
if stream:
# If we have a stream and either a JPEG or a WEBP, save them as those since those are a bit better than plain PNG
if 'jpeg' in img.content_type:
target = 'jpeg'
im = im.convert('RGB')
elif 'webp' in img.content_type:
target = 'webp'
# Janky goofy ahh solution to a stupid problem!
if tiny:
floc = imhash + '_tiny' + '.' + target
else:
floc = imhash + '.' + target
# If the file exists, just use it, that's what hashes are for.
if not os.path.exists(settings.MEDIA_ROOT + floc):
im.save(settings.MEDIA_ROOT + floc, target, optimize=True)
if not Imagefield:
return settings.MEDIA_URL + floc
# Not compatible with imagefield otherwise, if this is not here, Images uploaded will not show due to an incorrect URL.
# yes it's a crack den solution but it works.
else: return floc
# Todo: Put this into post/comment delete thingy method
def image_rm(image_url):
if settings.IMAGE_DELETE_SETTING:
if settings.MEDIA_URL in image_url:
sysfile = image_url.split(settings.MEDIA_URL)[1]
sysloc = settings.MEDIA_ROOT + sysfile
if settings.IMAGE_DELETE_SETTING > 1:
try:
remove(sysloc)
except:
return False
else:
return True
# The RM'd directory to move it to
rmloc = sysloc.replace(settings.MEDIA_ROOT, settings.MEDIA_ROOT + 'rm/')
try:
rename(sysloc, rmloc)
except:
return False
else:
return True
else:
return False
def get_gravatar(email): def get_gravatar(email):
try: try:
page = urllib.request.urlopen('https://gravatar.com/avatar/'+ md5(email.encode('utf-8').lower()).hexdigest() +'?d=404&s=128') page = urllib.request.urlopen('https://gravatar.com/avatar/'+ md5(email.encode('utf-8').lower()).hexdigest() +'?d=404&s=128')
+113 -236
View File
@@ -73,11 +73,15 @@ def community_list(request):
else: else:
ad = "no ads" ad = "no ads"
# announcements within the past week-ish # announcements within the past week-ish
announcements = Post.objects.filter(community__tags='announcements', spoils=False, created__gte=Now()-timedelta(days=5)).order_by('-created')[:6]
if request.user.is_authenticated: if request.user.is_authenticated:
if request.user.show_announcements:
announcements = Post.objects.filter(community__tags='announcements', spoils=False, created__gte=Now()-timedelta(days=5)).order_by('-created')[:6]
else:
announcements = None
my_communities = obj.filter(creator=request.user).order_by('-created')[0:12] my_communities = obj.filter(creator=request.user).order_by('-created')[0:12]
else: else:
my_communities = None my_communities = None
announcements = None
return render(request, 'closedverse_main/community_list.html', { return render(request, 'closedverse_main/community_list.html', {
'title': 'Communities', 'title': 'Communities',
'ad': ad, 'ad': ad,
@@ -180,25 +184,21 @@ def community_favorites(request):
}) })
def login_page(request): def login_page(request):
# rn the password form does not take into account if you have the old password format or not.
if request.user.is_authenticated: if request.user.is_authenticated:
return redirect('/') return redirect('/')
if request.method == 'POST': if request.method == 'POST':
incorrect_password = False form = sign_in(request.POST)
form = LoginForm(request.POST)
if form.is_valid(): if form.is_valid():
user = User.objects.get(username__iexact=form.cleaned_data['username']) authenticated_user, status = form.user
# no longer logs failed logins LoginAttempt.objects.create(user=authenticated_user, success=True, user_agent=request.META.get('HTTP_USER_AGENT'), addr=request.META.get('REMOTE_ADDR'))
# do I really want to fix that? request.session['passwd'] = authenticated_user.password
LoginAttempt.objects.create(user=user,success=True, user_agent=request.META.get('HTTP_USER_AGENT'),addr=request.META.get('REMOTE_ADDR')) login(request, authenticated_user)
request.session['passwd'] = user.password
login(request, user)
location = request.GET.get('next', '/') location = request.GET.get('next', '/')
if request.headers.get('x-requested-with') == 'XMLHttpRequest': if request.headers.get('x-requested-with') == 'XMLHttpRequest':
return HttpResponse(location) return HttpResponse(location)
return redirect(location) return redirect(location)
else: else:
form = LoginForm() form = sign_in()
return render(request, 'closedverse_main/login_page.html', { return render(request, 'closedverse_main/login_page.html', {
'title': 'Log in', 'title': 'Log in',
@@ -420,159 +420,21 @@ def user_view(request, username):
user = request.user user = request.user
profile = user.profile() profile = user.profile()
profile.setup(request) profile.setup(request)
comment_old = profile.comment form = profile_settings_page(request.POST, request.FILES)
nickname_old = user.nickname
if profile.cannot_edit:
return json_response("Not allowed.")
if len(request.POST.get('screen_name')) == 0 or len(request.POST['screen_name']) > 32:
return json_response('Nickname is too long or too short (length '+str(len(request.POST.get('screen_name')))+', max 32)')
if len(request.POST.get('profile_comment')) > 2200:
return json_response('Profile comment is too long (length '+str(len(request.POST.get('profile_comment')))+', max 2200)')
if len(request.POST.get('country')) > 255:
return json_response('Region is too long (length '+str(len(request.POST.get('country')))+', max 255)')
if len(request.POST.get('website')) > 255:
return json_response('Web URL is too long (length '+str(len(request.POST.get('website')))+', max 255)')
if len(request.POST.get('bg_url')) > 300:
return json_response('Background URL is too long (length '+str(len(request.POST.get('bg_url')))+', max 300)')
if len(request.POST.get('whatareyou')) > 300:
return json_response('"What Are You" is too long (length '+str(len(request.POST.get('whatareyou')))+', max 300)')
if len(request.POST.get('external')) > 255:
return json_response('Discord Tag is too long (length '+str(len(request.POST.get('external')))+', max 300)')
if len(request.POST.get('email')) > 500:
return json_response('Email is too long (length '+str(len(request.POST.get('email')))+', max 500)')
# Kinda unneeded but gdsjkgdfsg
if request.POST.get('website') == 'Web URL' or request.POST.get('country') == 'Region' or request.POST.get('external') == 'Discord Tag':
return json_response("I'm laughing right now.")
if len(request.POST.get('avatar')) > 255: if form.is_valid():
return json_response('Avatar is too long (length '+str(len(request.POST.get('avatar')))+', max 255)') email = form.cleaned_data.get('email')
if request.POST.get('email') and not request.POST.get('email') == 'None': file = form.cleaned_data.get('file')
if User.email_in_use(request.POST['email'], request): if email and User.objects.exclude(pk=user.pk).filter(email__iexact=email).exists():
return HttpResponseBadRequest("That email address is already in use, that can't happen.") return json_response('Email is taken already.')
try: if file and not profile.let_freedom:
EmailValidator()(value=request.POST['email']) return json_response('You aren\'t allowed to upload images.')
except ValidationError: # custom save function, because I am clinically insane
return json_response("Your e-mail address is invalid. Input an e-mail address, or input nothing.") form.save(user=request.user)
if User.nnid_in_use(request.POST.get('origin_id'), request):
return json_response("That Nintendo Network ID is already in use, that would cause confusion.")
#if user.has_plain_avatar():
# user.avatar = request.POST.get('avatar') or ''
# custom handler
if request.POST.get('avatar') == '2':
if request.FILES.get('screen'):
if not request.user.has_freedom():
return json_response("Not allowed.")
upload = None
if request.FILES.get('screen'):
# worth noting that the file for the avatar is never cleaned up after the user changes it
upload = util.image_upload(request.FILES['screen'], True, avatar=True)
if upload == 1:
return json_response("sorry, we are racist to the image you uploaded, you have to choose another one")
user.avatar = upload
user.has_mh = False
elif request.POST.get('avatar') == '1':
if not request.POST.get('origin_id'):
user.has_mh = False
profile.origin_id = None
profile.origin_info = None
user.avatar = ('s' if getrandbits(1) else '')
user.avatar = get_gravatar(user.email) or ('s' if getrandbits(1) else '')
user.has_mh = False
elif request.POST.get('avatar') == '0':
if not request.POST.get('origin_id'):
user.has_mh = False
profile.origin_id = None
profile.origin_info = None
user.avatar = ('s' if getrandbits(1) else '')
else:
if not request.POST.get('mh'):
return json_response('i think you gotta wait for the nnid to retrieve')
user.has_mh = True
#getmii = get_mii(request.POST.get('origin_id'))
#if not getmii:
# return json_response('NNID not found')
user.avatar = request.POST.get('mh')
#profile.origin_id = getmii[2]
profile.origin_id = 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:
validate_color(request.POST['color'])
except ValidationError:
user.color = None
else:
dark = True if user.color == '#000000' else False
light = True if user.color == '#ffffff' else False
if dark:
return json_response("Too dark")
elif light:
user.color = None
else:
if request.POST['color'] == '#ffffff':
user.color = None
else:
user.color = request.POST['color']
else:
user.color = None
# set the theme
if request.POST.get('theme'):
reset_theme = False if request.POST.get('reset-theme') is None else True
try:
validate_color(request.POST['theme'])
except ValidationError:
user.theme = None
else:
light = True if request.POST['theme'] == '#ffffff' else False
if light:
user.theme = None
elif reset_theme:
user.theme = None
else:
user.theme = request.POST['theme']
else:
user.theme = None
if request.POST.get('email') == 'None':
user.email = None
else:
user.email = request.POST.get('email')
profile.country = request.POST.get('country')
website = request.POST.get('website')
if ' ' in website or not '.' in website:
profile.weblink = ''
else:
profile.weblink = website
profile.comment = request.POST.get('profile_comment')
profile.external = request.POST.get('external')
profile.whatareyou = request.POST.get('whatareyou')
profile.relationship_visibility = (request.POST.get('relationship_visibility') or 0)
profile.id_visibility = (request.POST.get('id_visibility') or 0)
profile.yeahs_visibility = (request.POST.get('yeahs_visibility') or 0)
profile.pronoun_is = (request.POST.get('pronoun_dot_is') or 0)
profile.gender_is = (request.POST.get('gender_select') or 0)
profile.comments_visibility = (request.POST.get('comments_visibility') or 0)
profile.let_friendrequest = (request.POST.get('let_friendrequest') or 0)
user.bg_url = (request.POST.get('bg_url') or None)
user.nickname = filterchars(request.POST.get('screen_name'))
# Maybe todo?: Replace all "not .. == .." with ".. != .." etc
# If the user cannot edit and their nickname/avatar is different than what they had, don't let it happen.
if request.POST.get('profile_comment') != comment_old or request.POST.get('screen_name') != nickname_old:
ProfileHistory.objects.create(user=user,
old_nickname=nickname_old,
old_comment=comment_old,
new_nickname=request.POST.get('screen_name'),
new_comment=request.POST.get('profile_comment'))
if not user.email:
profile.email_login = 1
else:
profile.email_login = (request.POST.get('email_login') or 1)
profile.save()
user.save()
return HttpResponse() return HttpResponse()
else:
return json_response(form.errors.as_text())
posts = user.get_posts(3, 0, request, timezone.now()) posts = user.get_posts(3, 0, request, timezone.now())
yeahed = user.get_yeahed(0, 3, 0, request.user) yeahed = user.get_yeahed(0, 3, 0, request.user)
for yeah in yeahed: for yeah in yeahed:
@@ -979,7 +841,7 @@ def community_tools(request, community):
can_edit = the_community.can_edit_community(request.user) can_edit = the_community.can_edit_community(request.user)
if not can_edit: if not can_edit:
raise Http404() raise Http404()
form = CommunitySettingForm(instance=the_community) form = edit_community(instance=the_community)
return render(request, 'closedverse_main/community_tools.html', { return render(request, 'closedverse_main/community_tools.html', {
'title': 'Community tools', 'title': 'Community tools',
'form': form, 'form': form,
@@ -994,7 +856,7 @@ def community_tools_set(request, community):
can_edit = the_community.can_edit_community(request.user) can_edit = the_community.can_edit_community(request.user)
if not can_edit: if not can_edit:
return HttpResponseForbidden() return HttpResponseForbidden()
form = CommunitySettingForm(request.POST, request.FILES, instance=the_community) form = edit_community(request.POST, request.FILES, instance=the_community)
if not form.is_valid(): if not form.is_valid():
return json_response(form.errors.as_text()) return json_response(form.errors.as_text())
form.save() form.save()
@@ -1010,7 +872,7 @@ def community_create(request):
raise Http404() raise Http404()
if request.user.c_tokens < 1: if request.user.c_tokens < 1:
raise Http404() raise Http404()
form = CommunitySettingForm() form = edit_community()
return render(request, 'closedverse_main/community_create.html', { return render(request, 'closedverse_main/community_create.html', {
'title': 'Create a community', 'title': 'Create a community',
'form': form, 'form': form,
@@ -1022,7 +884,7 @@ def community_create_action(request):
return HttpResponseForbidden() return HttpResponseForbidden()
if request.user.c_tokens < 1: if request.user.c_tokens < 1:
return HttpResponseForbidden() return HttpResponseForbidden()
form = CommunitySettingForm(request.POST, request.FILES) form = edit_community(request.POST, request.FILES)
if not form.is_valid(): if not form.is_valid():
return json_response(form.errors.as_text()) return json_response(form.errors.as_text())
community = form.save() community = form.save()
@@ -1043,29 +905,28 @@ def post_create(request, community):
community = Community.objects.get(id=community) community = Community.objects.get(id=community)
except (Community.DoesNotExist, ValueError): except (Community.DoesNotExist, ValueError):
return HttpResponseNotFound() return HttpResponseNotFound()
# Method of Community
new_post = community.create_post(request) if not community.post_perm(request):
if not new_post: return json_response('You are not allowed to post here.')
return HttpResponseBadRequest() if request.user.unread_warning():
if isinstance(new_post, int): return json_response('Please check your notifications.')
# If post limit limit = request.user.limit_remaining()
if new_post == 8: if not limit is False and not limit > 0:
# then do meme return json_response('You are out of posts for today.')
return json_response("You have already exceeded the number of posts that you can contribute in a single day. Please try again tomorrow.", 1215919) del(limit)
return json_response({ if Post.real.filter(creator=request.user, created__gt=timezone.now() - timedelta(seconds=10)).exists():
1: "Your post is too long ("+str(len(request.POST['body']))+" characters, 2200 max).", return json_response('Please slow down.')
2: "The image you've uploaded is invalid.",
3: "You're posting too quickly, wait a few seconds and try again.", form = post_form(request.POST, request.FILES)
4: "Apparently, you're not allowed to post here.", if form.is_valid():
5: "Uh-oh, that URL wasn't valid..", if not request.user.has_freedom() and form.cleaned_data.get('file'):
6: "Not allowed.", return json_response('You\'re not allowed to post files.')
7: "Please don't spam.", new_post = form.save(commit=False)
9: "You're very funny. Unfortunately your funniness blah blah blah fuck off.", new_post.creator = request.user
10: "No mr white, you can't make a post entirely consistant of spaces", new_post.community = community
11: "The video you've uploaded is invalid.", new_post.save()
12: "Please don't post Zalgo text.", else:
13: "Please check your notifications.", return json_response(form.errors.as_text())
}.get(new_post))
# Render correctly whether we're posting to Activity Feed # Render correctly whether we're posting to Activity Feed
if community.is_activity(): if community.is_activity():
return render(request, 'closedverse_main/elements/community_post.html', { return render(request, 'closedverse_main/elements/community_post.html', {
@@ -1183,23 +1044,30 @@ def post_comments(request, post):
if request.method == 'POST': if request.method == 'POST':
# Wake # Wake
request.user.wake(request.META['REMOTE_ADDR']) request.user.wake(request.META['REMOTE_ADDR'])
# Method of Post # using forms now
new_post = post.create_comment(request)
if not new_post: if not post.can_comment(request):
return HttpResponseBadRequest() return json_response('You are not allowed to comment here.')
if isinstance(new_post, int): if request.user.unread_warning():
# If post limit return json_response('Please check your notifications.')
if new_post == 8: limit = request.user.limit_remaining()
# then do meme if not limit is False and not limit > 0:
return json_response("You have already exceeded the number of posts that you can contribute in a single day. Please try again tomorrow.", 1215919) return json_response('You are out of posts for today.')
return json_response({ del(limit)
1: "Your comment is too long ("+str(len(request.POST['body']))+" characters, 2200 max).", if Comment.real.filter(creator=request.user, created__gt=timezone.now() - timedelta(seconds=10)).exists():
2: "The image you've uploaded is invalid.", return json_response('Please slow down.')
3: "You're making comments too fast, wait a few seconds and try again.",
6: "Not allowed.", form = comment_form(request.POST, request.FILES)
12: "Please don't post Zalgo text.", if form.is_valid():
13: "Please check your notifications.", if not request.user.has_freedom() and form.cleaned_data.get('file'):
}.get(new_post)) return json_response('You\'re not allowed to post files.')
new_comment = form.save(commit=False)
new_comment.creator = request.user
new_comment.community = post.community
new_comment.original_post = post
new_comment.save()
else:
return json_response(form.errors.as_text())
# Give the notification! # Give the notification!
if post.is_mine(request.user): if post.is_mine(request.user):
users = [] users = []
@@ -1211,7 +1079,7 @@ def post_comments(request, post):
Notification.give_notification(request.user, 3, user, post) Notification.give_notification(request.user, 3, user, post)
else: else:
Notification.give_notification(request.user, 2, post.creator, post) Notification.give_notification(request.user, 2, post.creator, post)
return render(request, 'closedverse_main/elements/post-comment.html', { 'comment': new_post }) return render(request, 'closedverse_main/elements/post-comment.html', { 'comment': new_comment })
else: else:
comment_count = post.number_comments() comment_count = post.number_comments()
if comment_count > 20: if comment_count > 20:
@@ -1517,19 +1385,24 @@ def messages_view(request, username):
if request.method == 'POST': if request.method == 'POST':
# Wake # Wake
request.user.wake(request.META['REMOTE_ADDR']) request.user.wake(request.META['REMOTE_ADDR'])
new_post = conversation.make_message(request)
if not new_post: if request.user.unread_warning():
return HttpResponseBadRequest() return json_response('Please check your notifications.')
if isinstance(new_post, int): if Post.real.filter(creator=request.user, created__gt=timezone.now() - timedelta(seconds=3)).exists():
return json_response({ return json_response('Please slow down.')
1: "Your message is too long ("+str(len(request.POST['body']))+" characters, 2200 max).",
2: "The image you've uploaded is invalid.", form = message_form(request.POST, request.FILES)
3: "Sorry, but you're sending messages too fast.", if form.is_valid():
4: "Please check your notifications.", if not request.user.has_freedom() and form.cleaned_data.get('file'):
6: "Not allowed.", return json_response('You\'re not allowed to post files.')
}.get(new_post)) new_message = form.save(commit=False)
new_message.creator = request.user
new_message.conversation = conversation
new_message.save()
else:
return json_response(form.errors.as_text())
friendship.update() friendship.update()
return render(request, 'closedverse_main/elements/message.html', { 'message': new_post }) return render(request, 'closedverse_main/elements/message.html', { 'message': new_message })
else: else:
if request.GET.get('offset'): if request.GET.get('offset'):
messages = conversation.messages(request, 20, int(request.GET['offset'])) messages = conversation.messages(request, 20, int(request.GET['offset']))
@@ -1591,11 +1464,15 @@ def prefs(request):
request.user.hide_online = True request.user.hide_online = True
else: else:
request.user.hide_online = False request.user.hide_online = False
if request.POST.get('c'):
request.user.show_announcements = True
else:
request.user.show_announcements = False
profile.save() profile.save()
request.user.save() request.user.save()
return HttpResponse() return HttpResponse()
lights = not (request.session.get('lights', False)) lights = not (request.session.get('lights', False))
arr = [profile.let_yeahnotifs, lights, request.user.hide_online] arr = [profile.let_yeahnotifs, lights, request.user.hide_online, request.user.show_announcements]
return JsonResponse(arr, safe=False) return JsonResponse(arr, safe=False)
@login_required @login_required
@@ -1609,9 +1486,9 @@ def user_tools(request, username):
# check if the requesting user is allowed to change someone # check if the requesting user is allowed to change someone
if user.has_authority(request.user): if user.has_authority(request.user):
return HttpResponseForbidden() return HttpResponseForbidden()
user_form = User_tools_Form(instance=user) user_form = manage_user(instance=user)
profile_form = Profile_tools_Form(instance=profile) profile_form = manage_profile(instance=profile)
purge_form = PurgeForm() purge_form = purge_user()
accountmatch = User.objects.filter( accountmatch = User.objects.filter(
Q(addr=user.addr) | Q(addr=user.signup_addr) Q(addr=user.addr) | Q(addr=user.signup_addr)
@@ -1678,7 +1555,7 @@ def user_tools_warnings(request, username):
if user.has_authority(request.user): if user.has_authority(request.user):
return HttpResponseForbidden() return HttpResponseForbidden()
if request.method == 'POST': if request.method == 'POST':
form = Give_warning_form(request.POST) form = give_warning(request.POST)
if form.is_valid(): if form.is_valid():
warning = form.save(commit=False) warning = form.save(commit=False)
warning.to = user warning.to = user
@@ -1687,7 +1564,7 @@ def user_tools_warnings(request, username):
return redirect('main:user-view', user) return redirect('main:user-view', user)
unread_warnings = Notification.objects.filter(type=5, to=user, read=False)[:3] unread_warnings = Notification.objects.filter(type=5, to=user, read=False)[:3]
all_warnings = Warning.objects.filter(to=user).order_by('-id')[:8] all_warnings = Warning.objects.filter(to=user).order_by('-id')[:8]
form = Give_warning_form() form = give_warning()
return render(request, 'closedverse_main/man/manage_warnings.html', { return render(request, 'closedverse_main/man/manage_warnings.html', {
'user': user, 'user': user,
'unread_warnings': unread_warnings, 'unread_warnings': unread_warnings,
@@ -1707,7 +1584,7 @@ def user_tools_bans(request, username):
return HttpResponseForbidden() return HttpResponseForbidden()
if request.method == 'POST': if request.method == 'POST':
if not user.banned(): if not user.banned():
form = Give_Ban_Form(request.POST) form = give_ban(request.POST)
if form.is_valid(): if form.is_valid():
ban = form.save(commit=False) ban = form.save(commit=False)
ban.to = user ban.to = user
@@ -1717,15 +1594,15 @@ def user_tools_bans(request, username):
AuditLog.objects.create(type=5, user=user, by=request.user) AuditLog.objects.create(type=5, user=user, by=request.user)
return redirect('main:user-view', user) return redirect('main:user-view', user)
else: else:
form = Give_Ban_Form_Edit(request.POST, instance=user.active_ban()) form = edit_ban(request.POST, instance=user.active_ban())
ban = form.save(commit=False) ban = form.save(commit=False)
ban.save() ban.save()
AuditLog.objects.create(type=6, user=user, by=request.user) AuditLog.objects.create(type=6, user=user, by=request.user)
return redirect('main:user-view', user) return redirect('main:user-view', user)
if not user.banned(): if not user.banned():
form = Give_Ban_Form() form = give_ban()
else: else:
form = Give_Ban_Form_Edit(instance=user.active_ban()) form = edit_ban(instance=user.active_ban())
return render(request, 'closedverse_main/man/manage_bans.html', { return render(request, 'closedverse_main/man/manage_bans.html', {
'user': user, 'user': user,
'banned': user.banned(), 'banned': user.banned(),
@@ -1746,9 +1623,9 @@ def user_tools_set(request, username):
if user.has_authority(request.user): if user.has_authority(request.user):
return HttpResponseForbidden() return HttpResponseForbidden()
user_form = User_tools_Form(request.POST, instance=user) user_form = manage_user(request.POST, instance=user)
profile_form = Profile_tools_Form(request.POST, instance=profile) profile_form = manage_profile(request.POST, instance=profile)
purge_form = PurgeForm(request.POST) purge_form = purge_user(request.POST)
if purge_form.is_valid(): if purge_form.is_valid():
purge_posts = purge_form.cleaned_data["purge_posts"] purge_posts = purge_form.cleaned_data["purge_posts"]
@@ -1762,9 +1639,9 @@ def user_tools_set(request, username):
Post.real.filter(creator=user, status=5, is_rm=True).update(is_rm=False, status=0) 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) Comment.real.filter(creator=user, status=5, is_rm=True).update(is_rm=False, status=0)
if purge_posts == True: if purge_posts == True:
Post.real.filter(creator=user).update(is_rm=True, status=5) Post.real.filter(creator=user, is_rm=False).update(is_rm=True, status=5)
if purge_comments == True: if purge_comments == True:
Comment.real.filter(creator=user).update(is_rm=True, status=5) Comment.real.filter(creator=user, is_rm=False).update(is_rm=True, status=5)
if user_form.is_valid() and profile_form.is_valid(): if user_form.is_valid() and profile_form.is_valid():
user_form.save() user_form.save()
@@ -1876,7 +1753,7 @@ def my_data(request):
def change_password(request): def change_password(request):
user = request.user user = request.user
if request.method == 'POST': if request.method == 'POST':
form = Settomgs_Change_Password(request.POST) form = set_password(request.POST)
if form.is_valid(): if form.is_valid():
old = form.cleaned_data.get('Old_Password') old = form.cleaned_data.get('Old_Password')
new = form.cleaned_data.get('New_Password') new = form.cleaned_data.get('New_Password')
@@ -1888,7 +1765,7 @@ def change_password(request):
return json_response("Success! Now you can log in with your new password!", "Finished") return json_response("Success! Now you can log in with your new password!", "Finished")
return json_response(form.errors.as_text()) return json_response(form.errors.as_text())
else: else:
form = Settomgs_Change_Password(request.POST) form = set_password(request.POST)
return render(request, 'closedverse_main/change-password.html', { return render(request, 'closedverse_main/change-password.html', {
'user': user, 'user': user,
'form': form, 'form': form,
+68 -225
View File
@@ -10,218 +10,6 @@ var pjax_container = '#container';
// Edit: This used to match 'WebKit' but it's now Gecko because WebKit will have that in here anyway // Edit: This used to match 'WebKit' but it's now Gecko because WebKit will have that in here anyway
var webkit = navigator.userAgent.indexOf('WebKit') > 0 || navigator.userAgent.indexOf('Firefox') > 0; var webkit = navigator.userAgent.indexOf('WebKit') > 0 || navigator.userAgent.indexOf('Firefox') > 0;
function setupDrawboard() {
var canvas = document.getElementById("artwork-canvas");
var ctx = canvas.getContext('2d');
var haval = $("input[type=hidden][name=painting]");
if(haval.length) {
dds = new Image();
dds.src = "data:image/png;base64," + haval.val();
dds.onload = function() {
ctx.drawImage(dds,0,0);
};
}
var undoCanvas = document.getElementById('artwork-canvas-undo');
var undoCtx = undoCanvas.getContext('2d');
var redoCanvas = document.getElementById('artwork-canvas-redo');
var redoCtx = redoCanvas.getContext('2d');
undoCanvas.width = 320; undoCanvas.height = 120;
redoCanvas.width = 320; redoCanvas.height = 120;
canvas.width = 320; canvas.height = 120;
var mousePosOld = 0;
var artworkTool = {type: 0, size: 1};
var sizeSmall = 1;
var sizeMedium = 2;
var sizeLarge = 4;
var artworkColor = "#000000";
var artworkZoomFactor = 1;
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect();
if(evt.type == 'touchmove') {
var clientX = evt.touches[0].clientX;
var clientY = evt.touches[0].clientY;
} else {
var clientX = evt.clientX;
var clientY = evt.clientY;
}
return {
x: (clientX - rect.left) / artworkZoomFactor,
y: (clientY - rect.top) / artworkZoomFactor
};
}
function drawLineNoAliasing(ctx, sx, sy, tx, ty) {
var dist = Math.sqrt((tx-sx)*(tx-sx)+(ty-sy)*(ty-sy));
var ang = Math.atan((ty-sy)/((tx-sx)==0?0.01:(tx-sx)))+((tx-sx)<0?Math.PI:0);
for(var i=0;i<dist;i++) {
ctx.fillRect(Math.round(sx + Math.cos(ang)*i),
Math.round(sy + Math.sin(ang)*i),
artworkTool.size, artworkTool.size);
}
}
function artworkUpdate(evt) {
//console.log('artworkUpdate()');
var mousePos = getMousePos(evt);
if(artworkTool.type < 2) {
if(mousePosOld == 0) mousePosOld = mousePos;
if(evt.originalEvent.buttons == 1 || evt.type == 'touchmove') {
if(artworkTool.type == 0) {
ctx.fillStyle = artworkColor;
} else {
ctx.fillStyle = "#fff";
}
drawLineNoAliasing(ctx, mousePosOld.x,mousePosOld.y,mousePos.x,mousePos.y);
}
mousePosOld = mousePos;
}
}
function artworkDrawOnce(evt) {
//console.log('artworkDrawOnce()');
var mousePos = getMousePos(evt);
if(artworkTool.type < 2) {
if(evt.which == 1) {
if(artworkTool.type == 0) {
ctx.fillStyle = artworkColor;
} else {
ctx.fillStyle = "#fff";
}
ctx.fillRect(Math.round(mousePos.x), Math.round(mousePos.y), artworkTool.size, artworkTool.size);
}
} else {
ctx.fillStyle = artworkColor;
ctx.fillFlood(mousePos.x, mousePos.y, 0);
}
}
function artworkClear() {
doTheThing();
//console.log('artworkClear()');
artworkUndoDown();
//console.log('artworkUndoDown()');
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, 320, 120);
}
function artworkUndoDown() {
undoCtx.drawImage(canvas, 0, 0);
}
function artworkUndo() {
redoCtx.drawImage(canvas, 0, 0);
ctx.drawImage(undoCanvas, 0, 0);
undoCtx.drawImage(redoCanvas, 0, 0);
}
function artworkToolUpdate() {
//console.log('artworkToolUpdate()');
if($(this).hasClass('artwork-eraser')) {
var toolType = 1;
} else if($(this).hasClass('artwork-fill')) {
var toolType = 2;
} else {
var toolType = 0;
}
if($(this).hasClass('selected')) {
if($(this).hasClass('small')) {
artworkTool = {type: toolType, size: sizeMedium};
$(this).removeClass('small');
$(this).addClass('medium');
} else if ($(this).hasClass('medium')) {
artworkTool = {type: toolType, size: sizeLarge};
$(this).removeClass('medium');
$(this).addClass('large');
} else if ($(this).hasClass('large')) {
artworkTool = {type: toolType, size: sizeSmall};
$(this).removeClass('large');
$(this).addClass('small');
} else {
artworkTool = {type: toolType};
}
} else {
$('.memo-buttons button').removeClass('selected');
$(this).addClass('selected');
if($(this).hasClass('small')) {
artworkTool = {type: toolType, size: sizeSmall};
} else if ($(this).hasClass('medium')) {
artworkTool = {type: toolType, size: sizeMedium};
} else if ($(this).hasClass('large')) {
artworkTool = {type: toolType, size: sizeLarge};
} else {
artworkTool = {type: toolType};
}
}
}
function artworkZoomUpdate(evt) {
//console.log('artworkZoomUpdate()');
if(artworkZoomFactor == 1) {
artworkZoomFactor = 2;
$('#artwork-canvas').css('width', '640px');
} else if(artworkZoomFactor == 2) {
artworkZoomFactor = 4;
$('#artwork-canvas').css('width', '1280px');
$(this).addClass('out');
} else {
artworkZoomFactor = 1;
$('#artwork-canvas').css('width', '320px');
$(this).removeClass('out');
}
}
artworkClear();
$(document).on('mousemove', artworkUpdate);
$(document).on('touchstart', function(){
mousePosOld = 0;
});
$(document).on('touchmove', artworkUpdate);
$('#artwork-canvas').on('mousedown', artworkDrawOnce);
$('#artwork-canvas').on('mousedown', artworkUndoDown);
$('button').contextmenu(function() { $(this).click(); return false });
$('.artwork-clear').click(artworkClear);
$('.artwork-undo').click(artworkUndo);
$('.artwork-pencil, .artwork-eraser, .artwork-fill').click(artworkToolUpdate);
$(".artwork-color").spectrum({
color: "#000000",
preferredFormat: "hex",
showInput: true,
showPalette: true,
change: function(color) {
artworkColor = color;
},
palette: [["#000000", "#808080"], ["#ff0000",
"#804000"], ["#ff8000", "#c08000"], ["#ffff00",
"#fff8dc"], ["#00c700", "#008000"], ["#00ffff",
"#00a0a0"], ["#0000ff", "#0080ff"], ["#ff00ff",
"#800080"]]
});
$('.artwork-zoom').click(artworkZoomUpdate);
function doTheThing(){
var canvas = document.getElementById("artwork-canvas");
var ctx = canvas.getContext('2d');
//console.log('doing the thing');
var pixelArray = [];
for(var i = 0; i < canvas.clientHeight; i++) {
pixelArray[i] = [];
for(var j = 0; j < canvas.clientWidth; j++) {
var data = ctx.getImageData(j, i, 1, 1).data;
if(data[0] + data[1] + data[2] < 382) {
pixelArray[i].push(0);
} else {
pixelArray[i].push(1);
}
}
}
}
$('.memo-finish-btn').on('click',function(){
var dataURL = canvas.toDataURL();
if(typeof dataURL !== undefined) {
var img = dataURL.split(",")[1];
//$("input[type=hidden][name=painting]").val(b.Closed.chksum(img) + img);
$("input[type=hidden][name=painting]").val(img);
}
$("#drawing").remove();
$(".textarea-memo").append("<img id=\"drawing\" src=\"" + dataURL + "\" style=\"background:white;\"></img>");
$("#memo-drawboard-page button").off('click');
$('body').css('overflow', '');
});
}
var fixe = false; var fixe = false;
function openDrawboardModal() { function openDrawboardModal() {
if(innerWidth <= 800) { if(innerWidth <= 800) {
@@ -441,6 +229,10 @@ var Olv = Olv || {};
} }
var g = new Olv.ModalWindow($('.linkc'));g.open(); var g = new Olv.ModalWindow($('.linkc'));g.open();
}, },
dlConf: function() {
$('#container').prepend('<div class="dialog linkc none"><div class=dialog-inner><div class=window><h1 class=window-title>Download file?</h1><div class=window-body><p class=window-body-content>You are about to download a file.<br>Are you sure you want to download <b>'+ass+'</b>?</p><div class=form-buttons><button class="olv-modal-close-button gray-button" type=button data-event-type=ok onclick="$(\'.linkc\').remove()">No</button><button class="olv-modal-close-button black-button" type=button onclick="Olv.Net.lo(\''+ass+'\');$(\'.linkc\').remove()">Yes</button></div></div></div></div></div>');
var g = new Olv.ModalWindow($('.linkc'));g.open();
},
changesel: function(a) { changesel: function(a) {
$("li#global-menu-" + a).addClass("selected"); $("li#global-menu-" + a).addClass("selected");
} }
@@ -504,6 +296,11 @@ var Olv = Olv || {};
error_code: "Not Allowed (Forbidden)", error_code: "Not Allowed (Forbidden)",
message: "Try refreshing the page or logging back in.\n" message: "Try refreshing the page or logging back in.\n"
} }
case 413:
return {
error_code: "File size too large.",
message: "Big brother it's too big, I can't take it!\n"
}
break; break;
case 500: case 500:
errmsg = "An error has been encountered in the server.\n"; errmsg = "An error has been encountered in the server.\n";
@@ -1473,6 +1270,11 @@ var Olv = Olv || {};
g.preventDefault(); g.preventDefault();
b.Closed.prlinkConf(); b.Closed.prlinkConf();
}); });
a('.download-confirm').on('click', function(g) {
ass = a(this).attr('href');
g.preventDefault();
b.Closed.dlConf();
});
} }
, ,
b.Entry.toggleEmpathy = function(a) { b.Entry.toggleEmpathy = function(a) {
@@ -2351,11 +2153,54 @@ var Olv = Olv || {};
e.preventDefault(); e.preventDefault();
$.ajax({url: '/pref', $.ajax({url: '/pref',
success: function(a) { success: function(a) {
// Assigning 'checked' attribute based on the response array 'a'
yeah_notifications = a[0] ? ' checked' : ''; yeah_notifications = a[0] ? ' checked' : '';
lights_off = a[1] ? ' checked' : ''; lights_off = a[1] ? ' checked' : '';
online_status = a[2] ? ' checked' : ''; online_status = a[2] ? ' checked' : '';
show_announcements = a[3] ? ' checked' : '';
$('#wrapper').prepend('<div class="dialog acc-set none"><div class=dialog-inner><div class=window><h1 class=window-title>Account preferences</h1><div class=window-body><form id=feedback-form><p class=window-body-content> These are your account\'s preferences, pretty self-explanatory.</p><br> <input type=checkbox value=1 name=a'+ yeah_notifications +'> Enable notifications for Yeahs<br><input type=checkbox onclick=Olv.Closed.lights()'+ lights_off +'> Enable dark mode<br> <input type=checkbox value=1 name=b'+ online_status +'> Hide my latest time seen and online status from others</form><div class=form-buttons><button class="olv-modal-close-button gray-button" type=button data-event-type=ok onclick="$(\'.acc-set\').remove()">Cancel</button><button class="black-button ac-send" type="button">Save</button></div></div></div></div></div>'); // Prepending the form HTML to the '#wrapper' element
$('#wrapper').prepend(`
<div class="dialog acc-set none">
<div class="dialog-inner">
<div class="window">
<h1 class="window-title">Account preferences</h1>
<div class="window-body">
<form id="feedback-form">
<p class="window-body-content">
These are your account's preferences, pretty self-explanatory.
</p>
<br>
<input type="checkbox" value="1" name="a"${yeah_notifications}>
Enable notifications for Yeahs
<br>
<input type="checkbox" onclick="Olv.Closed.lights()"${lights_off}>
Enable dark mode
<br>
<input type="checkbox" value="1" name="b"${online_status}>
Hide my latest time seen and online status from others
<br>
<input type="checkbox" value="1" name="c"${show_announcements}>
Show the announcements list on the front page.
</form>
<div class="form-buttons">
<button
class="olv-modal-close-button gray-button"
type="button"
data-event-type="ok"
onclick="$('.acc-set').remove()"
>
Cancel
</button>
<button class="black-button ac-send" type="button">
Save
</button>
</div>
</div>
</div>
</div>
</div>
`);
var g = new b.ModalWindow($('.acc-set'));g.open(); var g = new b.ModalWindow($('.acc-set'));g.open();
$('.ac-send').on('click',function() { $('.ac-send').on('click',function() {
b.Form.post('/pref', $('#feedback-form').serializeArray()) b.Form.post('/pref', $('#feedback-form').serializeArray())
@@ -2734,6 +2579,11 @@ mode_post = 0;
msg_rm_load(); msg_rm_load();
}); });
}); });
$('.download-confirm').on('click', function(g) {
ass = $(this).attr('href');
g.preventDefault();
b.Closed.dlConf();
});
}), }),
b.router.connect("^/communities/(?:favorites|played)$", function(a, c, d) { b.router.connect("^/communities/(?:favorites|played)$", function(a, c, d) {
b.Closed.changesel("community"); b.Closed.changesel("community");
@@ -2926,16 +2776,8 @@ $('.post-poll .poll-votes').on('click', function() {
$('.cancel-button').on('click',function(){et()}) $('.cancel-button').on('click',function(){et()})
b.EntryForm.setupFormStatus(t, e); b.EntryForm.setupFormStatus(t, e);
$('.edit-post-button').on('click',function(){ $('.edit-post-button').on('click',function(){
if($('.post-content-memo').length) {
b.showMessage("", "You can't edit a drawing, sorry.");
} if($('.screenshot-container > video').length) {
b.showMessage("", "You can't edit your uploaded video, sorry. But you can edit the contents of the post.");
et(); et();
b.Form.toggleDisabled(submit_btn, true); b.Form.toggleDisabled(submit_btn, true);
} else {
et();
b.Form.toggleDisabled(submit_btn, true);
}
}) })
submit_btn.on('click',function(a) { submit_btn.on('click',function(a) {
a.preventDefault(); a.preventDefault();
@@ -3045,12 +2887,8 @@ mode_post = 0;
$('.cancel-button').on('click',function(){et()}) $('.cancel-button').on('click',function(){et()})
b.EntryForm.setupFormStatus(t, e); b.EntryForm.setupFormStatus(t, e);
$('.edit-post-button').on('click',function(){ $('.edit-post-button').on('click',function(){
if($('.reply-content-memo').length) {
b.showMessage("", "You can't edit a drawing, sorry.")
} else {
et(); et();
b.Form.toggleDisabled(submit_btn, true) b.Form.toggleDisabled(submit_btn, true)
}
}) })
submit_btn.on('click',function(a) { submit_btn.on('click',function(a) {
a.preventDefault() a.preventDefault()
@@ -3061,6 +2899,11 @@ mode_post = 0;
}) })
}) })
} }
a('.download-confirm').on('click', function(g) {
ass = a(this).attr('href');
g.preventDefault();
b.Closed.dlConf();
});
rm_btn = $('.rm-post-button') rm_btn = $('.rm-post-button')
if(rm_btn.length) { if(rm_btn.length) {
rm_btn.on('click',function(){ rm_btn.on('click',function(){