Login page moved to forms.py, New warning and ban system, a bunch of other shit.
- Change password page from profile settings has been moved over to forms.py - Login page has been moved over to forms.py. There is no JavaScript right now, however it is still functional. -Profile_tools_Form has been altered to remove a few unneeded fields that administrators don't need to mess with. - warned_reason and warned fields are gone from User_tools_Form and will be removed from models.py at a later point. - a new ban system has been implemented. While bans are still incomplete and lack capabilities to ban IP addresses as of right now, the groundwork is now here and those features can be added later. - manage_bans page was added. - A new warning system has been implemented. If a Warning is created, a notification is created automatically with it. A warning notification replaces the "new announcement" notification type. - Warning notifications are larger and stand out compared to normal notifications - When posting, commenting or messaging someone, a check is performed to make sure you've seen the warning notification. - manage_warnings page was added. This page will also show you the warning history. - help_text was added to a bunch of fields.
@@ -1,21 +1,39 @@
|
||||
Stuff that is done:
|
||||
- Disable comments button
|
||||
- merge with closedverse-video-support (mostly)
|
||||
- Prevent users from posting in communities created by blocked users.
|
||||
- Hide email password reset form if no SMTP server is set up. (done by Arian Kordi)
|
||||
- Fix icon and banner uploads.
|
||||
Icons and banners use util.image_upload now, at the cost of having to set them all again.
|
||||
|
||||
Stuff that is in progress
|
||||
- Removing shitty code.
|
||||
- Adding help_text, and placeholders for clarity, this will be seen on the admin panel and even forms too.
|
||||
- moving to forms.py
|
||||
So far, user_tools and community_tools have been moved over.
|
||||
- New Warning system.
|
||||
Warnings are here, And there's an interface for warning users.
|
||||
When a warning is made, a notification is also made.
|
||||
Replaced the 5th notification type with warnings instead of announcements.
|
||||
A check is made to make sure request.user does not have unread warnings before they can post.
|
||||
There's no javascript for the form page yet.
|
||||
Audit logs are not set up, but the Warning model shows who warned each user.
|
||||
|
||||
Stuff that I want
|
||||
- User friendly interface for admins
|
||||
Admins should be able to manage users quickly and effectively without having to worry about a clusterfuck interface.
|
||||
Primary actions like warning users, banning users, and whatnot should be an easy few clicks away.
|
||||
|
||||
- New Ban system.
|
||||
Bans are here also with its own interface.
|
||||
Instead of entering a datetime field manually, Admins are provided with choises for different ban lengths when using the frontend.
|
||||
IP bans and range bans are not added yet.
|
||||
A proper ban page is not added yet and simply shows a 403 error.
|
||||
If a ban page is added, it should show the reason, and when the ban will expire.
|
||||
There's no javascript for the form page yet.
|
||||
Audit logs are not set up, but the Ban model shows who banned each user.
|
||||
|
||||
- Removing shitty code.
|
||||
- moving to forms.py.
|
||||
The login page, admin tools, and community tools are using forms.py.
|
||||
|
||||
Ideas:
|
||||
- Image and video file boxes in one.
|
||||
- Pinning posts????
|
||||
Not needed at all if I'm being honest.
|
||||
- A new user metadata page that does not suck.
|
||||
- Make it, so you need to enter your current password to change your email address.
|
||||
- Ways for mods to view who invited who.
|
||||
- Full ImageField integration
|
||||
- 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)
|
||||
@@ -29,7 +29,7 @@ DEBUG = False
|
||||
# Do not include 127.0.0.1 or localhost
|
||||
# in production for security reasons.
|
||||
ALLOWED_HOSTS = [
|
||||
'127.0.0.1',
|
||||
'',
|
||||
]
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
@@ -63,10 +63,8 @@ MIDDLEWARE = [
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
|
||||
#'ban.middleware.BanManagement',
|
||||
'closedverse_main.middleware.ClosedMiddleware',
|
||||
#'maintenance.middleware.MaintenanceManagement',
|
||||
'closedverse_main.middleware.CheckForBanMiddleware',
|
||||
]
|
||||
if not DEBUG:
|
||||
MIDDLEWARE += ['whitenoise.middleware.WhiteNoiseMiddleware']
|
||||
@@ -103,30 +101,6 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
# log errors.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'handlers': {
|
||||
'file': {
|
||||
'level': 'ERROR',
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': 'logs/errors.log',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['file'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
|
||||
|
||||
@@ -168,15 +142,6 @@ USE_L10N = False
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# You can use Mailtrap to get the email system working. Mailtrap is free and will work well for what you'll be doing with this.
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = None
|
||||
EMAIL_HOST_USER = None
|
||||
EMAIL_HOST_PASSWORD = None
|
||||
EMAIL_PORT = None
|
||||
EMAIL_USE_TLS = True
|
||||
DEFAULT_FROM_EMAIL = None
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
@@ -246,7 +211,7 @@ RECAPTCHA_PUBLIC_KEY = None
|
||||
RECAPTCHA_PRIVATE_KEY = None
|
||||
|
||||
# Key for IPHub service for Closedverse, which detects proxies.
|
||||
IPHUB_KEY = ""
|
||||
IPHUB_KEY = None
|
||||
# If this is set to True, then users will receive an error
|
||||
# upon trying to sign up for the site behind a proxy.
|
||||
# Uses IPHub service and requires an API key defined above.
|
||||
@@ -282,25 +247,17 @@ invite_only = False
|
||||
|
||||
# Minimum level required to view IP addresses and user agents. (default: 10)
|
||||
# Mods under this level will still be able to manage users, however will not be able to view any sensitive data.
|
||||
min_lvl_metadata_perms = 100
|
||||
# Set this to 0 to prevent anyone from viewing data.
|
||||
min_lvl_metadata_perms = 0
|
||||
|
||||
# if someone's level is equal or above this, they can edit most community on your clone.
|
||||
# if someone's level is equal or above this, they can edit the most communities on your clone.
|
||||
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.
|
||||
level_needed_to_man_users = 5
|
||||
|
||||
<<<<<<< Updated upstream
|
||||
# file size limits in megabytes! only applies when using the community tools.
|
||||
max_icon_size = .5
|
||||
max_banner_size = 1
|
||||
# minimum_password_length is removed as the AUTH_PASSWORD_VALIDATORS work as of 08/2023
|
||||
|
||||
# The minimum length required for a user's password. This is to save the users from themselves in the event of a data breach. The longer and more complex the password is, the harder it is to be cracked. (default: 7)
|
||||
# This will definitely miss a few people off who just want to sign up without worrying about long passwords.
|
||||
minimum_password_length = 7
|
||||
|
||||
=======
|
||||
>>>>>>> Stashed changes
|
||||
# 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
|
||||
|
||||
@@ -323,7 +280,8 @@ memo_msg = """
|
||||
<h2>Cedar</h2>
|
||||
<p>what</p>
|
||||
<h2>Why is this person rehosting it?</h2>
|
||||
<p>im bored</p>"""
|
||||
<p>im bored</p>
|
||||
"""
|
||||
|
||||
# This option enables some production-specific pages
|
||||
# and routines, such as HTTPS scheme redirection and
|
||||
|
||||
@@ -21,12 +21,12 @@ class UserForm(ModelForm):
|
||||
'password': PasswordInput(),
|
||||
}
|
||||
"""
|
||||
@admin.action(description='Hide selected items')
|
||||
def Hide_Memo(modeladmin, request, queryset):
|
||||
queryset.update(show = False)
|
||||
@admin.action(description='Show selected items')
|
||||
def Show_Memo(modeladmin, request, queryset):
|
||||
queryset.update(show = True)
|
||||
@admin.action(description='Void selected Invites')
|
||||
def Void_invite(modeladmin, request, queryset):
|
||||
queryset.update(void = True)
|
||||
@admin.action(description='Restore selected Invites')
|
||||
def Restore_invite(modeladmin, request, queryset):
|
||||
queryset.update(void = False, used = False)
|
||||
@admin.action(description='Hide selected items')
|
||||
def Hide_content(modeladmin, request, queryset):
|
||||
queryset.update(is_rm = True)
|
||||
@@ -60,7 +60,7 @@ def Enable_user(modeladmin, request, queryset):
|
||||
|
||||
|
||||
class UserAdmin(admin.ModelAdmin):
|
||||
search_fields = ('id', 'unique_id', 'username', 'nickname', 'email', )
|
||||
search_fields = ('id', 'username', 'nickname', 'email', )
|
||||
list_display = ('id', 'username', 'nickname', 'warned', 'level', 'staff', 'active', )
|
||||
exclude = ('addr', 'signup_addr', 'password', )
|
||||
actions = [Disable_user, Enable_user]
|
||||
@@ -68,20 +68,27 @@ class UserAdmin(admin.ModelAdmin):
|
||||
# Not yet
|
||||
#form = UserForm
|
||||
class ProfileAdmin(admin.ModelAdmin):
|
||||
search_fields = ('id', 'unique_id', 'origin_id', )
|
||||
search_fields = ('id', 'user__username__icontains', 'comment', 'origin_id',)
|
||||
raw_id_fields = ('user', 'favorite',)
|
||||
list_display = ('id', 'user', 'comment', 'let_freedom',)
|
||||
|
||||
class InvitesAdmin(admin.ModelAdmin):
|
||||
search_fields = ('creator__username', 'used_by__username', 'code', )
|
||||
raw_id_fields = ('creator', 'used_by', )
|
||||
list_display = ('creator', 'used_by', 'code', 'used', 'void', )
|
||||
actions = [Void_invite, Restore_invite]
|
||||
|
||||
class ComplaintAdmin(admin.ModelAdmin):
|
||||
search_fields = ('id', 'unique_id', 'body', )
|
||||
search_fields = ('id', 'body', )
|
||||
raw_id_fields = ('creator', )
|
||||
|
||||
class ConversationAdmin(admin.ModelAdmin):
|
||||
search_fields = ('id', 'unique_id', )
|
||||
search_fields = ('id', )
|
||||
raw_id_fields = ('source', 'target', )
|
||||
|
||||
class PostAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('creator', 'poll', )
|
||||
search_fields = ('id', 'unique_id', 'body', 'creator__username', )
|
||||
search_fields = ('id', 'body', 'creator__username', )
|
||||
list_display = ('id', 'creator', 'body', 'is_rm', )
|
||||
actions = [Hide_content, Show_content, Disable_comments, Enable_comments]
|
||||
def get_queryset(self, request):
|
||||
@@ -89,7 +96,7 @@ class PostAdmin(admin.ModelAdmin):
|
||||
|
||||
class CommentAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('creator', 'original_post', )
|
||||
search_fields = ('id', 'unique_id', 'body', 'creator__username', )
|
||||
search_fields = ('id', 'body', 'creator__username', )
|
||||
list_display = ('id', 'creator', 'body', 'original_post', 'is_rm', )
|
||||
actions = [Hide_content, Show_content]
|
||||
def get_queryset(self, request):
|
||||
@@ -98,14 +105,14 @@ class CommentAdmin(admin.ModelAdmin):
|
||||
class CommunityAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('creator', )
|
||||
list_display = ('id', 'name', 'description', 'type', 'creator', 'popularity', 'is_rm', 'is_feature', 'require_auth')
|
||||
search_fields = ('id', 'unique_id', 'name', 'description', )
|
||||
search_fields = ('id', 'name', 'description', )
|
||||
actions = [Hide_content, Show_content, Feature_community, Unfeature_community, force_login, unforce_login]
|
||||
def get_queryset(self, request):
|
||||
return models.Community.real.get_queryset()
|
||||
|
||||
class MessageAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('creator', 'conversation', )
|
||||
search_fields = ('id', 'unique_id', 'body', 'creator__username', )
|
||||
search_fields = ('id', 'body', 'creator__username', )
|
||||
list_display = ('id', 'creator', 'conversation', 'body', )
|
||||
actions = [Hide_content, Show_content]
|
||||
def get_queryset(self, request):
|
||||
@@ -113,19 +120,16 @@ class MessageAdmin(admin.ModelAdmin):
|
||||
|
||||
class NotificationAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('to', 'source', 'context_post', 'context_comment',)
|
||||
search_fields = ('unique_id', )
|
||||
search_fields = ('to__username', 'source__username', 'context_post__body', 'context_comment__body',)
|
||||
list_display = ('id', 'to', 'source', 'context_post', 'context_comment',)
|
||||
|
||||
class AuditAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('by', 'user', 'post', 'comment', 'reversed_by', )
|
||||
search_fields = ('by__username', 'user__username', )
|
||||
raw_id_fields = ('by', 'user', 'post', 'comment', 'community', 'reversed_by', )
|
||||
search_fields = ('by__username', 'user__username', 'post__body', 'comment__body', 'community__name', )
|
||||
|
||||
class AdsAdmin(admin.ModelAdmin):
|
||||
raw_id_fileds = ('id', 'created', 'url', 'imageurl')
|
||||
|
||||
class InvitesAdmin(admin.ModelAdmin):
|
||||
raw_id_fileds = ('id', 'created', 'creator')
|
||||
|
||||
class YeahAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('by', 'post', 'comment', )
|
||||
list_display = ('by', 'post', 'comment', )
|
||||
@@ -145,11 +149,13 @@ admin.site.unregister(Group)
|
||||
admin.site.register(models.Role, RoleAdmin)
|
||||
admin.site.register(models.User, UserAdmin)
|
||||
admin.site.register(models.Profile, ProfileAdmin)
|
||||
admin.site.register(models.Invites, InvitesAdmin)
|
||||
admin.site.register(models.Community, CommunityAdmin)
|
||||
admin.site.register(models.Complaint, ComplaintAdmin)
|
||||
admin.site.register(models.Message, MessageAdmin)
|
||||
admin.site.register(models.Conversation, ConversationAdmin)
|
||||
admin.site.register(models.Notification, NotificationAdmin)
|
||||
#admin.site.register(models.LoginAttempt, LoginAdmin)
|
||||
admin.site.register(models.UserBlock)
|
||||
admin.site.register(models.AuditLog, AuditAdmin)
|
||||
admin.site.register(models.ProfileHistory, HistoryAdmin)
|
||||
@@ -157,17 +163,9 @@ admin.site.register(models.ProfileHistory, HistoryAdmin)
|
||||
|
||||
admin.site.register(models.Post, PostAdmin)
|
||||
admin.site.register(models.Comment, CommentAdmin)
|
||||
<<<<<<< Updated upstream
|
||||
admin.site.register(models.Ads, AdsAdmin)
|
||||
admin.site.register(models.welcomemsg, WelcomemsgAdmin)
|
||||
admin.site.register(models.Yeah, YeahAdmin)
|
||||
admin.site.register(models.Follow)
|
||||
admin.site.register(models.FriendRequest)
|
||||
admin.site.register(models.Friendship, ConversationAdmin)
|
||||
admin.site.register(models.Invites, InvitesAdmin)
|
||||
admin.site.register(models.Poll)
|
||||
admin.site.register(models.PollVote)
|
||||
=======
|
||||
|
||||
admin.site.register(models.Ban)
|
||||
admin.site.register(models.Warning)
|
||||
|
||||
if settings.DEBUG:
|
||||
admin.site.register(models.Yeah)
|
||||
@@ -178,4 +176,3 @@ if settings.DEBUG:
|
||||
admin.site.register(models.Poll)
|
||||
admin.site.register(models.PollVote)
|
||||
admin.site.register(models.Ads, AdsAdmin)
|
||||
>>>>>>> Stashed changes
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from django import forms
|
||||
from .models import *
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.timezone import timedelta
|
||||
from closedverse import settings
|
||||
|
||||
# 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 CommunitySettingForm(forms.ModelForm):
|
||||
@@ -21,20 +24,61 @@ class CommunitySettingForm(forms.ModelForm):
|
||||
'community_banner'
|
||||
)
|
||||
|
||||
class Settomgs_Change_Password(forms.Form):
|
||||
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'}))
|
||||
Confirm_Password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Confirm Password'}))
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
old = cleaned_data.get('Old_Password')
|
||||
new = cleaned_data.get('New_Password')
|
||||
confirm = cleaned_data.get('Confirm_Password')
|
||||
if not old or not new or not confirm:
|
||||
raise forms.ValidationError('Please fill out all the fields.')
|
||||
if old == new:
|
||||
raise forms.ValidationError('The old password and new password can\'t be the same.')
|
||||
if new != confirm:
|
||||
raise forms.ValidationError('Passwords do not match.')
|
||||
try:
|
||||
validate_password(new)
|
||||
except forms.ValidationError as error:
|
||||
raise forms.ValidationError("your password fucking sucks!")
|
||||
return cleaned_data
|
||||
class PurgeForm(forms.Form):
|
||||
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.')
|
||||
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 UserForm(forms.ModelForm):
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'class': 'auth-input', 'placeholder': 'Username'}))
|
||||
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'auth-input', 'placeholder': 'Password'}))
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(LoginForm, self).clean()
|
||||
username = cleaned_data.get('username')
|
||||
password = cleaned_data.get('password')
|
||||
|
||||
user = User.objects.authenticate(username=username, password=password)
|
||||
|
||||
if user is None:
|
||||
raise forms.ValidationError("The user doesn't exist.")
|
||||
elif user[1] == False:
|
||||
raise forms.ValidationError("Invalid password.", code='invalid')
|
||||
elif user[1] == 2:
|
||||
raise forms.ValidationError("This account's password needs to be reset. Contact an admin or reset by email.", code='required_reset')
|
||||
elif not user[0].is_active():
|
||||
raise forms.ValidationError("This account was disabled.", code='disabled')
|
||||
return cleaned_data
|
||||
|
||||
class User_tools_Form(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.')
|
||||
c_tokens = forms.IntegerField(min_value = 0, max_value = 100, required = False, help_text = 'The remaining C-Tokens this user has.')
|
||||
has_mh = forms.BooleanField(required = False, label='Use Mii', help_text='Don\'t fuck with this please.')
|
||||
avatar = forms.CharField(required = False, help_text = 'If \"Use Mii\" is turned on, The Mii ID should reside here, otherwise any good old URL will do.')
|
||||
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.')
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'nickname', 'role', 'c_tokens', 'hide_online', 'active', 'can_invite', 'warned', 'has_mh', 'avatar', 'warned_reason']
|
||||
fields = ['username', 'nickname', 'role', 'c_tokens', 'hide_online', 'active', 'can_invite', 'has_mh', 'avatar']
|
||||
|
||||
def clean_username(self):
|
||||
username = self.cleaned_data.get('username')
|
||||
@@ -42,11 +86,43 @@ class UserForm(forms.ModelForm):
|
||||
raise forms.ValidationError("The username contains invalid characters.")
|
||||
return username
|
||||
|
||||
|
||||
class ProfileForm(forms.ModelForm):
|
||||
class Profile_tools_Form(forms.ModelForm):
|
||||
# applying classes is super weird.
|
||||
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.')
|
||||
class Meta:
|
||||
model = Profile
|
||||
fields = ['comment', 'country', 'whatareyou', 'weblink', 'external', 'let_freedom', 'limit_post', 'cannot_edit', 'pronoun_is', 'id_visibility', 'let_friendrequest', 'yeahs_visibility', 'comments_visibility']
|
||||
fields = ['comment', 'country', 'whatareyou', 'weblink', 'external', 'let_freedom', 'limit_post', 'cannot_edit']
|
||||
|
||||
class Give_warning_form(forms.ModelForm):
|
||||
reason = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'textarea'}))
|
||||
# Super simple, does not need to be it's own form but whatever.
|
||||
class Meta:
|
||||
model = Warning
|
||||
fields = ['reason']
|
||||
|
||||
class Give_Ban_Form(forms.ModelForm):
|
||||
BAN_OPTIONS = [
|
||||
(1, '1 Day'),
|
||||
(2, '2 Days'),
|
||||
(3, '3 Days'),
|
||||
(7, '1 Week'),
|
||||
(14, '2 Weeks'),
|
||||
(30, '1 Month'),
|
||||
(60, '2 Months'),
|
||||
(365, '1 Year'),
|
||||
(None, 'Forever'),
|
||||
]
|
||||
# In the future we can add options for IP bans and shit.
|
||||
reason = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'textarea'}))
|
||||
expiry_date = forms.ChoiceField(required=False, choices=BAN_OPTIONS, initial=1)
|
||||
|
||||
def clean_expiry_date(self):
|
||||
expiry_choice = self.cleaned_data['expiry_date']
|
||||
if expiry_choice:
|
||||
return timezone.now() + timedelta(days=int(expiry_choice))
|
||||
else:
|
||||
return timezone.now() + timedelta(days=365.25*100) # same exact thing done in indigo.
|
||||
class Meta:
|
||||
model = Ban
|
||||
fields = ['reason', 'expiry_date']
|
||||
@@ -2,6 +2,8 @@ from django.http import HttpResponseForbidden
|
||||
from closedverse import settings
|
||||
from django.shortcuts import redirect
|
||||
from django.contrib.auth import logout
|
||||
from .models import Ban
|
||||
from django.utils import timezone
|
||||
from re import compile
|
||||
|
||||
# Taken from https://python-programming.com/recipes/django-require-authentication-pages/
|
||||
@@ -10,6 +12,31 @@ if settings.FORCE_LOGIN:
|
||||
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
|
||||
EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]
|
||||
|
||||
class CheckForBanMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
||||
|
||||
# If the user is not authenticated, there's no need to check for bans
|
||||
if not request.user.is_authenticated:
|
||||
return None
|
||||
|
||||
# Get one active ban that is not expired
|
||||
active_ban = Ban.objects.filter(
|
||||
to=request.user,
|
||||
active=True,
|
||||
expiry_date__gte=timezone.now()
|
||||
).first()
|
||||
|
||||
if active_ban:
|
||||
return HttpResponseForbidden('You are banned from this site. Reason: ' + active_ban.reason)
|
||||
return None
|
||||
|
||||
class ClosedMiddleware(object):
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
@@ -42,13 +42,12 @@ class UserManager(BaseUserManager):
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
def closed_create_user(self, username, password, email, addr, signup_addr, user_agent, nick, nn, gravatar):
|
||||
def closed_create_user(self, username, password, email, addr, signup_addr, nick, nn, gravatar):
|
||||
user = self.model(
|
||||
username = username,
|
||||
nickname = util.filterchars(nick),
|
||||
addr = addr,
|
||||
signup_addr = signup_addr,
|
||||
user_agent = user_agent,
|
||||
email = email,
|
||||
)
|
||||
profile = Profile.objects.model()
|
||||
@@ -125,7 +124,7 @@ class Role(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
# determines whether to fetch role from static or media, for built-in roles
|
||||
is_static = models.BooleanField(default=False)
|
||||
image = models.ImageField(upload_to='roles/', max_length=100, help_text='Upload an icon that will show on the top right of one\'s profile.')
|
||||
image = models.ImageField(upload_to='roles/', max_length=100, help_text='Upload an icon that will show on the top left of one\'s profile. A 22x22 image works best!')
|
||||
organization = models.CharField(max_length=255, blank=True, null=True, help_text='Text that shows above one\'s username')
|
||||
|
||||
def __str__(self):
|
||||
@@ -143,39 +142,26 @@ class User(AbstractBaseUser):
|
||||
nickname = models.CharField(max_length=64, null=True)
|
||||
password = models.CharField(max_length=128)
|
||||
email = models.EmailField(null=True, blank=True, default='')
|
||||
has_mh = models.BooleanField(default=False)
|
||||
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 = models.CharField(max_length=1200, blank=True, default='')
|
||||
theme = ColorField(blank=True, null=True)
|
||||
# LEVEL: 0-1 is default, everything else is just levels
|
||||
level = models.SmallIntegerField(default=0)
|
||||
# ROLE: This doesn't have anything
|
||||
<<<<<<< Updated upstream
|
||||
role = models.SmallIntegerField(default=0, choices=((0, 'normal'), (1, 'bot'), (2, 'administrator'), (3, 'moderator'), (4, 'openverse'), (5, 'donator'), (6, 'cool'), (7, 'urapp'), (8, 'owner'), (9, 'badgedes'), (10, 'jack'), (11, 'verified'),))
|
||||
=======
|
||||
#role = models.SmallIntegerField(default=0, choices=((0, 'normal'), (1, 'bot'), (2, 'administrator'), (3, 'moderator'), (4, 'openverse'), (5, 'donator'), (6, 'cool'), (7, 'urapp'), (8, 'owner'), (9, 'badgedes'), (10, 'jack'), (11, 'verified'),))
|
||||
level = models.SmallIntegerField(default=0, help_text='The rank of the user, \"0\" is by default. Users with a high enough rank will be able to manage users.')
|
||||
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.')
|
||||
>>>>>>> Stashed changes
|
||||
addr = models.CharField(max_length=64, null=True, blank=True)
|
||||
signup_addr = models.CharField(max_length=64, null=True, blank=True)
|
||||
user_agent = models.TextField(null=True, blank=True)
|
||||
# C Tokens are things that let you make communities and shit.
|
||||
<<<<<<< Updated upstream
|
||||
c_tokens = models.IntegerField(default=1)
|
||||
protect_data = models.BooleanField(default=False)
|
||||
=======
|
||||
c_tokens = models.IntegerField(default=1, help_text='How many communities should this user be allowed to make?')
|
||||
protect_data = models.BooleanField(default=False, null=True, help_text='Prevent people from lookin\' at private data for this user.')
|
||||
>>>>>>> Stashed changes
|
||||
|
||||
# Things that don't have to do with auth lol
|
||||
hide_online = models.BooleanField(default=False)
|
||||
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)
|
||||
|
||||
staff = models.BooleanField(default=False)
|
||||
active = models.BooleanField(default=True)
|
||||
can_invite = models.BooleanField(default=True)
|
||||
warned = models.BooleanField(default=False)
|
||||
warned_reason = models.CharField(blank=True, null=True, max_length=600)
|
||||
staff = models.BooleanField(default=False, help_text='Allow this user to access the admin panel you\'re using right now?')
|
||||
active = models.BooleanField(default=True, help_text='If this is off, the user is basically banned and can\'t do shit here')
|
||||
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.')
|
||||
warned = models.BooleanField(default=False, help_text='Shows an annoying fucking warning box on the front page.')
|
||||
warned_reason = models.CharField(blank=True, null=True, max_length=600, help_text='This string will show if the user is banned, or warned.')
|
||||
bg_url = models.CharField(max_length=300, null=True, blank=True)
|
||||
|
||||
is_anonymous = False
|
||||
@@ -209,12 +195,6 @@ class User(AbstractBaseUser):
|
||||
return self.warned
|
||||
def get_warned_reason(self):
|
||||
return self.warned_reason
|
||||
"""
|
||||
def set_password(self, raw_password):
|
||||
self.password = bcrypt_sha256.using(rounds=13).hash(raw_password)
|
||||
def check_password(self, raw_password):
|
||||
return bcrypt_sha256.using(rounds=13).verify(raw_password, hash=self.password)
|
||||
"""
|
||||
def profile(self, thing=None):
|
||||
# If thing is specified, that field is retrieved
|
||||
if thing:
|
||||
@@ -236,6 +216,8 @@ class User(AbstractBaseUser):
|
||||
except:
|
||||
return None
|
||||
return infodecode[0]
|
||||
def unread_warning(self):
|
||||
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
|
||||
@@ -261,25 +243,8 @@ class User(AbstractBaseUser):
|
||||
return int(limit) - recent_posts
|
||||
|
||||
def get_class(self):
|
||||
"""
|
||||
first = {
|
||||
1: '/s/img/bot.png',
|
||||
2: '/s/img/administrator.png',
|
||||
3: '/s/img/moderator.png',
|
||||
9: '/s/img/badgedes.png',
|
||||
}.get(self.role, '')
|
||||
second = {
|
||||
1: "Bot",
|
||||
2: "Administrator",
|
||||
3: "Moderator",
|
||||
9: "Badge Designer",
|
||||
}.get(self.role, '')
|
||||
"""
|
||||
#if first:
|
||||
# first = 'official ' + first
|
||||
if not self.role:
|
||||
return [None, None]
|
||||
#first = self.role.image
|
||||
second = self.role.organization
|
||||
first = self.role.image.url
|
||||
if self.role.is_static:
|
||||
@@ -429,7 +394,7 @@ class User(AbstractBaseUser):
|
||||
def notification_read(self):
|
||||
return self.notification_to.filter(read=False).update(read=True)
|
||||
def get_notifications(self):
|
||||
return self.notification_to.select_related('context_post').select_related('context_comment').select_related('source').filter().order_by('-latest')[0:64]
|
||||
return self.notification_to.select_related('context_post').select_related('context_comment').select_related('context_warning').select_related('source').filter().order_by('-latest')[0:64]
|
||||
def notifications_clean(self):
|
||||
""" Broken - gives OperationError on MySQL
|
||||
notif_get = self.notification_to.all().values_list('id', flat=True)
|
||||
@@ -617,6 +582,43 @@ class User(AbstractBaseUser):
|
||||
return False
|
||||
return user
|
||||
|
||||
# This will be a fucking pain in the ass!
|
||||
class Ban(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
by = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE, related_name='banned_by')
|
||||
to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='banned_user')
|
||||
reason = models.TextField(null=True, blank=True)
|
||||
expiry_date = models.DateTimeField(help_text='The date and time on which this ban will expire, Set this way off into the future if this ban should be permanent')
|
||||
active = models.BooleanField(default=True, help_text='Untick this to disable this ban')
|
||||
|
||||
def is_expired(self):
|
||||
if timezone.now() > self.expiry_date:
|
||||
return True
|
||||
return False
|
||||
def __str__(self):
|
||||
return "Ban for " + str(self.to)
|
||||
|
||||
# The new warning system, Warnings do not contribute to bans, they just show as warnings.
|
||||
# Can also be used as a means of reporting incident history.
|
||||
class Warning(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='Warned_by', help_text="This is not shown to the recipient")
|
||||
to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='Warned_user')
|
||||
reason = models.TextField(null=True, blank=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# check if the object is being created or updated
|
||||
is_new = self.pk is None
|
||||
super().save(*args, **kwargs)
|
||||
# create new notification only after new warning is created
|
||||
if is_new:
|
||||
Notification.give_notification(user=self.by, type=5, to=self.to, warning=self)
|
||||
|
||||
def __str__(self):
|
||||
return "Warning for " + str(self.to)
|
||||
|
||||
# An invite system, for closed off communities or for whatever reason.
|
||||
class Invites(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
@@ -640,21 +642,20 @@ class Invites(models.Model):
|
||||
class Community(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(blank=True, default='')
|
||||
ico = models.ImageField(upload_to='icon/%y/%m/%d/', max_length=100, blank=True, null=True)
|
||||
banner = models.ImageField(upload_to='banner/%y/%m/%d/', max_length=100, blank=True, null=True)
|
||||
description = models.TextField(blank=True)
|
||||
ico = models.ImageField(null=True, blank=True)
|
||||
banner = models.ImageField(null=True, blank=True)
|
||||
# Type: 0 - general, 1 - game, 2 - special
|
||||
type = models.SmallIntegerField(default=0, choices=((0, 'General'), (1, 'Game'), (2, 'Special'), (3, 'User Community'), (4, 'Hide')))
|
||||
# Platform - 0/none, 1/3DS, 2/Wii U, 3/both
|
||||
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, 'both'), (5, 'PC'), (6, 'Xbox'), (7, 'Playstation')))
|
||||
tags = models.CharField(blank=True, null=True, max_length=255, choices=(('announcements', 'main announcement community'), ('changelog', 'main changelog'), ('activity', 'Activity Feed posting community'), ('general', 'General Discussion Community')))
|
||||
tags = models.CharField(blank=True, help_text='Provides special functionality for specific communities.', null=True, max_length=255, choices=(('announcements', 'main announcement community'), ('changelog', 'main changelog'), ('activity', 'Activity Feed posting community'), ('general', 'General Discussion Community')))
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
is_rm = models.BooleanField(default=False)
|
||||
is_feature = models.BooleanField(default=False)
|
||||
require_auth = models.BooleanField(default=False)
|
||||
rank_needed_to_post = models.IntegerField(default=0)
|
||||
creator = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
|
||||
is_rm = models.BooleanField(default=False, help_text='Tick this on to remove the community.')
|
||||
is_feature = models.BooleanField(default=False, help_text='Feature this community on the front page where everyone can see it.')
|
||||
require_auth = models.BooleanField(default=False, help_text='Force your users to sign in to view this community.')
|
||||
rank_needed_to_post = models.IntegerField(default=0, help_text='Make admin only communities.')
|
||||
creator = models.ForeignKey(User, blank=True, null=True, help_text='Who owns this community? It\'s highly recommended to not fill this on for general communities. Keep in mind that owners can block others from using communities they own.', on_delete=models.CASCADE)
|
||||
|
||||
objects = PostManager()
|
||||
real = models.Manager()
|
||||
@@ -668,7 +669,7 @@ class Community(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
def icon(self):
|
||||
if self.ico and hasattr(self.ico, 'url'):
|
||||
if self.ico:
|
||||
return self.ico.url
|
||||
else:
|
||||
return settings.STATIC_URL + "img/title-icon-default.png"
|
||||
@@ -802,6 +803,8 @@ class Community(models.Model):
|
||||
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
|
||||
@@ -866,14 +869,14 @@ class Post(models.Model):
|
||||
url = models.URLField(max_length=1200, null=True, blank=True, default='')
|
||||
spoils = models.BooleanField(default=False)
|
||||
disable_yeah = models.BooleanField(default=False)
|
||||
lock_comments = models.SmallIntegerField(default=0, choices=((0, 'Not locked'), (1, 'Locked by user'), (2, 'Locked by mod')))
|
||||
lock_comments = models.SmallIntegerField(default=0, choices=((0, 'Not locked'), (1, 'Locked by user'), (2, 'Locked by mod')), help_text='People will not be able to comment on posts that are locked.')
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
edited = models.DateTimeField(auto_now=True)
|
||||
befores = models.TextField(null=True, blank=True)
|
||||
poll = models.ForeignKey('Poll', null=True, blank=True, on_delete=models.CASCADE)
|
||||
befores = models.TextField(null=True, blank=True, help_text='If this post gets edited, the old body will be here.')
|
||||
poll = models.ForeignKey('Poll', null=True, blank=True, on_delete=models.CASCADE, help_text='A busted ass poll feature that does not work.')
|
||||
has_edit = models.BooleanField(default=False)
|
||||
is_rm = models.BooleanField(default=False)
|
||||
status = models.SmallIntegerField(default=0, choices=post_status)
|
||||
is_rm = models.BooleanField(default=False, help_text='Tick this to hide this post')
|
||||
status = models.SmallIntegerField(default=0, choices=post_status, help_text='Used to distinguish how a post was removed. Our purging feature uses this to determine what posts to restore and what not to.')
|
||||
creator = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
objects = PostManager()
|
||||
@@ -1044,6 +1047,8 @@ class Post(models.Model):
|
||||
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'):
|
||||
@@ -1145,8 +1150,8 @@ class Comment(models.Model):
|
||||
edited = models.DateTimeField(auto_now=True)
|
||||
befores = models.TextField(null=True, blank=True)
|
||||
has_edit = models.BooleanField(default=False)
|
||||
is_rm = models.BooleanField(default=False)
|
||||
status = models.SmallIntegerField(default=0, choices=post_status)
|
||||
is_rm = models.BooleanField(default=False, help_text='Tick this to hide this comment')
|
||||
status = models.SmallIntegerField(default=0, choices=post_status, help_text='Used to distinguish how a comment was removed. Our purging feature uses this to determine what comments to restore and what not to.')
|
||||
creator = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
|
||||
|
||||
objects = PostManager()
|
||||
@@ -1278,8 +1283,8 @@ class Profile(models.Model):
|
||||
#birthday = models.DateField(null=True, blank=True)
|
||||
id_visibility = models.SmallIntegerField(default=0, choices=visibility)
|
||||
|
||||
pronoun_is = models.IntegerField(default=0, choices=(
|
||||
(0, "I don't know"), (1, "He/him"), (2, "She/her"), (3, "He/she"), (4, "It"), (5, "They/Them")
|
||||
pronoun_is = models.IntegerField(default=0, help_text='haha attack helicopter.', choices=(
|
||||
(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)
|
||||
@@ -1292,11 +1297,11 @@ class Profile(models.Model):
|
||||
favorite = models.ForeignKey(Post, blank=True, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
let_yeahnotifs = models.BooleanField(default=True)
|
||||
let_freedom = models.BooleanField(default=True)
|
||||
let_freedom = models.BooleanField(default=True, help_text='Restrict this user from posting images, videos, URLs, and even making new accounts.')
|
||||
# Todo: When you see this, implement it; make it a bool that determines whether the user should be able to edit their avatar; if this is true and
|
||||
#let_avatar = models.BooleanField(default=False)
|
||||
# Post limit, 0 for none
|
||||
limit_post = models.SmallIntegerField(default=0)
|
||||
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.')
|
||||
email_login = models.SmallIntegerField(default=1, choices=((0, 'Do not allow'), (1, 'Okay'), (2, 'Only allow')))
|
||||
@@ -1389,11 +1394,12 @@ class Notification(models.Model):
|
||||
(2, 'Comment on my post'),
|
||||
(3, 'Comment on others\' post'),
|
||||
(4, 'Follow to me'),
|
||||
(5, 'New Announcement'),
|
||||
(5, 'Warning received'),
|
||||
))
|
||||
merges = models.TextField(blank=True, default='')
|
||||
context_post = models.ForeignKey(Post, null=True, blank=True, on_delete=models.CASCADE)
|
||||
context_comment = models.ForeignKey(Comment, null=True, blank=True, on_delete=models.CASCADE)
|
||||
context_warning = models.ForeignKey(Warning, null=True, blank=True, on_delete=models.CASCADE)
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
latest = models.DateTimeField(auto_now=True)
|
||||
@@ -1401,6 +1407,8 @@ class Notification(models.Model):
|
||||
def __str__(self):
|
||||
return "Notification from " + str(self.source) + " to " + str(self.to) + " with type \"" + self.get_type_display() + "\""
|
||||
def url(self):
|
||||
if self.type == 5:
|
||||
return None
|
||||
what_type = {
|
||||
0: 'main:post-view',
|
||||
1: 'main:comment-view',
|
||||
@@ -1462,11 +1470,13 @@ class Notification(models.Model):
|
||||
self.source.is_following = False
|
||||
# In the future, please put giving notifications for classes into their respective classes (right now they're in views)
|
||||
@staticmethod
|
||||
def give_notification(user, type, to, post=None, comment=None):
|
||||
def give_notification(user, type, to, post=None, comment=None, warning=None):
|
||||
# Just keeping this simple for now, might want to make it better later
|
||||
# If the user sent a notification to this user at least 5 seconds ago, return False
|
||||
# Or if user is to
|
||||
# Or if yeah notifications are off and this is a yeah notification
|
||||
if type == 5:
|
||||
return user.notification_sender.create(type=type, to=to, context_warning=warning)
|
||||
user_is_self_unk = (not type == 3 and user == to)
|
||||
is_notification_too_fast = (user.notification_sender.filter(created__gt=timezone.now() - timedelta(seconds=5), type=type).exclude(type=4) and not type == 3)
|
||||
user_no_yeahnotif = (not to.let_yeahnotifs() and (type == 0 or type == 1))
|
||||
@@ -1606,6 +1616,8 @@ class Conversation(models.Model):
|
||||
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'):
|
||||
@@ -1771,10 +1783,11 @@ class UserBlock(models.Model):
|
||||
class AuditLog(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
type = models.SmallIntegerField(choices=((0, "Post delete"), (1, "Comment delete"), (2, "User edit"), (3, "Disable comments"), (4, "User delete"), (5, "Image delete"), (6, "Purge 1"), (7, "Purge 2"), (8, "Purge 3"), (9, "Purge 4"), (10, "Purge 5"), (11, "Un-purge 1"), (12, 'Changed server settings'), ))
|
||||
type = models.SmallIntegerField(choices=((0, "Post delete"), (1, "Comment delete"), (2, "User edit"), (3, "Disable comments"), (4, "Community edit"), ))
|
||||
post = models.ForeignKey(Post, related_name='audit_post', null=True, on_delete=models.CASCADE)
|
||||
comment = models.ForeignKey(Comment, related_name='audit_comment', null=True, on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(User, related_name='audit_user', null=True, on_delete=models.CASCADE)
|
||||
community = models.ForeignKey(Community, related_name='audit_community', null=True, on_delete=models.CASCADE)
|
||||
reasoning = models.TextField(null=True, blank=True, default="")
|
||||
by = models.ForeignKey(User, related_name='audit_by', on_delete=models.CASCADE)
|
||||
reversed_by = models.ForeignKey(User, null=True, related_name='audit_reverse_by', on_delete=models.CASCADE)
|
||||
|
||||
@@ -4,21 +4,12 @@
|
||||
<div id="help" class="main-column">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">Change password</h2>
|
||||
<form class="setting-form" method="post" action={% url "main:change-password-set" %}>
|
||||
<form class="setting-form" method="post" action={% url "main:change-password" %}>
|
||||
<p>You can change your password here</p>
|
||||
<li class="setting">
|
||||
<p class="settings-label">Old Password:</p>
|
||||
<div class="center center-input">
|
||||
<input type="password" name="old-password" maxlength="100" placeholder="Old Password">
|
||||
</div>
|
||||
<p class="settings-label">New Password:</p>
|
||||
<div class="center center-input">
|
||||
<input type="password" name="new-password" maxlength="100" placeholder="New Password">
|
||||
</div>
|
||||
<p class="settings-label">Confirm Password:</p>
|
||||
<div class="center center-input">
|
||||
<input type="password" name="confirm-password" maxlength="100" placeholder="Confirm Password">
|
||||
</div>
|
||||
<li class="center-input">
|
||||
{% for field in form %}
|
||||
<h3 class='label'>{{ field }}</h3>
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% csrf_token %}
|
||||
<div class="form-buttons">
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
</div>
|
||||
<div class="community-top-sidebar">
|
||||
<div class="post-list-outline index-memo">
|
||||
<h2 class="label">Community creation Q&A</h2>
|
||||
<h2 class="label">Community creation</h2>
|
||||
<h2>Why?</h2>
|
||||
<p>I have no idea, I guess it's cool to have. You can have your own little corner of this site now...</p>
|
||||
<h2>What can I do with my own community?</h2>
|
||||
<p>You can edit it, and that's basically it.</p>
|
||||
<p>Maybe there's a community that's missing which should be here, or you just want a corner of the site to call your own.</p>
|
||||
<h2>How can I make my own community?</h2>
|
||||
<p>Assuming you haven't already made one, A button should be visible below that will allow you to create a community. Don't worry, you'll be able to set the icon and banner when editing a community after it has been made. Just keep in mind that creating inappropriate, or shitty communities can result in them getting taken down, if this does happen you will not be refunded your C-Token, and you're shit out of luck.</p>
|
||||
<h2>What are C-Tokens, and how do I get them?</h2>
|
||||
<p>Community tokens are a stupid and overcomplicated method of preventing 200,000 communities from being made by one person. You get one when signing up, and you can use it to make your own community. Please do not ask staff for more C-Tokens unless you believe the communities you are going to make are really that good.</p>
|
||||
<h2>Any future plans?</h2>
|
||||
<p>no</p>
|
||||
<p>Community tokens are a stupid and overcomplicated method of preventing a bunch of communities from being made by one person. You get one when signing up, and you can use it to make your own community. Please do not ask staff for more C-Tokens, you probably won't be given any, and let's be honest here, one community is enough for one person.</p>
|
||||
<h2>Okay great... Now what can I do with my community?</h2>
|
||||
<p>Whatever you want, really... So long as your community isn't so shitty that we take it down. You are able to edit your community, set your banner, icon and whatnot. And you can ban people from posting and replying in your community if you block someone. Just keep in mind that blocks work both ways, they cannot fuck with you, nor can you fuck with them.</p>
|
||||
</div>
|
||||
{% if request.user.c_tokens > 0 %}<a class="big-button" href={% url "main:community-create" %}><span class="symbol-label">Create a community</span></a>{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -7,22 +7,7 @@
|
||||
<form action="{% url "main:community-search" %}" class="search">
|
||||
<input maxlength="32" name="query" placeholder="Search all communities" type="text"><input title="Search" type="submit" value="q">
|
||||
</form>
|
||||
<!--<div class="close-announce-container">
|
||||
<div class="close-announce-link">
|
||||
<a class="title" id="close-text-ann" href="/posts/1186"><b>Closedverse is still in indefinite maintenance.</b></a>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
{% if user.is_warned and user.is_active %}
|
||||
<div class="notice" style="background-color: #ffc783;border: 1px solid #ffb358;">
|
||||
<p><b>WARNING</b>: You have been issued a warning by an administrator.
|
||||
<div>
|
||||
{% if user.get_warned_reason %}
|
||||
<p>Reason: "{{ user.get_warned_reason }}"</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not user.is_active and user.is_authenticated %}
|
||||
<div class="notice" style="background-color: #ff9797;border: 1px solid #ff5252;">
|
||||
<p><b>Oops</b>: You've been smacked by an admin. Better luck next time.
|
||||
@@ -44,6 +29,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if announcements and request.user.is_authenticated %}
|
||||
<a href="#communities">Skip to communities</a>
|
||||
<div class="tleft">
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">Latest Announcements</h2>
|
||||
|
||||
@@ -20,23 +20,19 @@
|
||||
{% if request.user.has_freedom %}
|
||||
<li class="setting-community-icon">
|
||||
<label class="file-button-container">
|
||||
<p class="input-label">Community icon: {{ max_icon_size }}MB</p>
|
||||
<p class="input-label">Community icon:</p>
|
||||
<span class="button file-upload-button">Upload a new icon</span>
|
||||
<input accept="image/gif, image/png, image/webp, image/jpeg, image/jpg, image/svg+xml, image/bmp" type="file" style="display: none;" name="community_icon" onchange="loadFile(event)" id="upload-file">
|
||||
<input accept="image/gif, image/png, image/webp, image/jpeg, image/jpg, image/svg+xml, image/bmp" type="file" style="display: none;" name="community_icon" id="upload-file">
|
||||
</label>
|
||||
</li>
|
||||
<li class="setting-community-banner">
|
||||
<label class="file-button-container">
|
||||
<p class="input-label">Community banner: {{ max_banner_size }}MB</p>
|
||||
<p class="input-label">Community banner:</p>
|
||||
<span class="button file-upload-button">Upload a new banner</span>
|
||||
<input accept="image/gif, image/png, image/webp, image/jpeg, image/jpg, image/svg+xml, image/bmp" type="file" style="display: none;" name="community_banner" onchange="loadFile(event)" id="upload-file">
|
||||
</label>
|
||||
</li>
|
||||
<<<<<<< Updated upstream
|
||||
{% endif %}
|
||||
=======
|
||||
{% endif %}
|
||||
>>>>>>> Stashed changes
|
||||
<li>
|
||||
<p> </p>
|
||||
<input type="checkbox" name="force_login" {% if community.require_auth %}checked{% endif %}>Require login:
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
{% extends "closedverse_main/layout.html" %}{% block main-body %}{% load closedverse_community %}
|
||||
{% community_sidebar community request %}
|
||||
<div class="main-column">
|
||||
{% if user.is_warned %}
|
||||
<div class="notice" style="background-color: #ffc783;border: 1px solid #ffb358;">
|
||||
<p><b>WARNING</b>: You have been issued a warning by an administrator.
|
||||
<div>
|
||||
{% if user.get_warned_reason %}
|
||||
<p>Reason: "{{ user.get_warned_reason }}"</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="post-list-outline">
|
||||
<h2 class="label">{{ community.name }}</h2>
|
||||
{% if request.user.is_authenticated and community.post_perm %}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<p>I'm loading it !</p>
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</label>
|
||||
<div class="file-button-container">
|
||||
<span class="input-label">Video <span>MP4s and WEBMs are allowed. (50MB size limit)</span></span>
|
||||
<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>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% if message.drawing %}
|
||||
<p class="post-content-memo"><img src="{{ message.drawing }}"></p>
|
||||
{% else %}
|
||||
<div class="post-content-text">{{ message.body|markdown }}</div>
|
||||
<div class="post-content-text">{{ message.body }}</div>
|
||||
{% endif %}
|
||||
{% if message.screenshot %}
|
||||
<div class="screenshot-container still-image"><img src="{{ message.screenshot }}"></div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% load closedverse_tags %}<div class="news-list-content trigger{% if not notification.read %} notify{% endif %}" tabindex="0" id="{{ notification.unique_id }}" data-href="{{ notification.url }}">
|
||||
{% user_icon_container notification.source %}
|
||||
{% load closedverse_tags %}<div class="news-list-content trigger{% if not notification.read %} notify{% endif %}{% if notification.type == 5 %} bigger{% endif %}" tabindex="0" id="{{ notification.unique_id }}" {% if notification.url %}data-href="{{ notification.url }}"{% endif %}>
|
||||
{% if not notification.type == 5 %}{% user_icon_container notification.source %}{% endif %}
|
||||
<div class="body">
|
||||
{% if notification.type == 0 %}
|
||||
{% print_names notification.all_users %} gave <a href="{{ notification.url }}" class="link">your post ({{ notification.context_post.trun|truncatechars:30 }})</a> a Yeah.
|
||||
@@ -11,6 +11,10 @@
|
||||
{% print_names notification.all_users %} commented on <a href="{{ notification.url }}" class="link">{{ notification.source.nickname }}'s post ({{ notification.context_post.trun|truncatechars:30 }})</a>.
|
||||
{% elif notification.type == 4 %}
|
||||
Followed by {% print_names notification.all_users %}.
|
||||
{% elif notification.type == 5 %}
|
||||
<p class='warning-notif'>You've received a warning from an administrator!</p>
|
||||
{% if notification.context_warning.reason %}<p class='warning-reason'><b>Reason: </b>{{ notification.context_warning.reason }}</p>{% endif %}
|
||||
<p class='warning-note'>To avoid seeing this in the future, it's a good idea to catch up on the rules. Continuous violations of our rules may result in your account getting restricted.</p>
|
||||
{% endif %}
|
||||
<span class="timestamp"> {% time notification.latest %}</span>
|
||||
{% if notification.type == 4 and not notification.source.is_following and not notification.all_users|length > 1 %}
|
||||
|
||||
@@ -12,17 +12,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_warned and user.is_active %}
|
||||
<div class="notice" style="background-color: #ffc783;border: 1px solid #ffb358;">
|
||||
<p><b>Notice</b>: This user has received a warning by an administrator.</p>
|
||||
<div>
|
||||
{% if user.get_warned_reason %}
|
||||
<p>Reason: "{{ user.get_warned_reason }}"</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="sidebar-container">
|
||||
{% if profile.favorite.screenshot %}
|
||||
<a href="{% url "main:post-view" profile.favorite_id %}" id="sidebar-cover" style="background-image:url({{ profile.favorite.screenshot }})">
|
||||
@@ -50,13 +39,10 @@
|
||||
<button type="button" data-action="{% url "main:user-fr-delete" user.username %}" data-screen-name="{{ user.nickname }}" class="friend-button unf delete button symbol">Friends</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if not has_authority and request.user.can_manage %}
|
||||
<button class="button" data-href="{% url "main:user-tools" user.username %}">User Settings</button>
|
||||
{% endif %}
|
||||
{% if not general and not user.is_me and profile.can_block %}
|
||||
{% if not user.is_me %}
|
||||
<div class="report-buttons-content">
|
||||
<!-- for later use <button type="button" class="report-button report-user" data-track-label="user" data-track-action="openReportModal" data-track-category="reportViolator" data-modal-open="#report-violator-page">Report Violation</button> -->
|
||||
<button type="button" class="report-button block-button">{% if profile.is_blocked %}Unblock{% else %}Block{% endif %}</button>
|
||||
{% if profile.can_block %}<button type="button" class="report-button block-button">{% if profile.is_blocked %}Unblock{% else %}Block{% endif %}</button>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -124,6 +110,21 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if not has_authority and request.user.can_manage %}
|
||||
<div class="sidebar-setting sidebar-container">
|
||||
<div class="sidebar-post-menu">
|
||||
<a href={% url "main:user-tools" user.username %} class="sidebar-admin-settings symbol{% if selection == 7 %} selected{% endif %}">
|
||||
<span>User Settings</span>
|
||||
</a>
|
||||
<a href={% url "main:user-tools-warnings" user.username %} class="sidebar-admin-warnings symbol{% if selection == 8 %} selected{% endif %}">
|
||||
<span>Warn User</span>
|
||||
</a>
|
||||
<a href={% url "main:user-tools-bans" user.username %} class="sidebar-admin-bans symbol{% if selection == 9 %} selected{% endif %}">
|
||||
<span>Ban User</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="sidebar-container sidebar-profile">
|
||||
{% if profile.comment %}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<h3>General account information:</h3>
|
||||
<p>IP address: <span>{% if user.addr %}{{ user.addr }}{% else %}Data missing{% endif %}
|
||||
<p>Signup IP address: <span>{% if user.signup_addr %}{{ user.signup_addr }}{% else %}Data missing{% endif %}</p>
|
||||
<p>User agent: <span>{% if user.user_agent %}{{ user.user_agent }}{% else %}Data missing{% endif %}
|
||||
<p>Rank: <span>{% if user.staff %}You are a staff member.{% elif not user.level <= 0 %}You are a moderator. (Level {{ user.level }}){% else %}You are a regular user.{% endif %}
|
||||
<h3>My content:</h3>
|
||||
<p>Your account has existed for {{ age }} days! During that time, we've collected:</p>
|
||||
|
||||
@@ -7,12 +7,18 @@
|
||||
<p class="lh">Log In</p>
|
||||
<p>Sign in to access this instance of {{ brand_name }}</p>
|
||||
<div class="login-box">
|
||||
<h3 class="label"><input type="text" class="auth-input" name="username" maxlength="32" placeholder="Username"></h3>
|
||||
<h3 class="label"><input type="password" class="auth-input" name="password" maxlength="32" placeholder="Password"></h3>
|
||||
<h3 class='label'>{{ form.username }}</h3>
|
||||
<h3 class='label'>{{ form.password }}</h3>
|
||||
</div>
|
||||
{% csrf_token %}
|
||||
|
||||
<p class="red" style="margin-bottom:6px"></p>
|
||||
<p class="red" style="margin-bottom:6px">
|
||||
{% for field, errors in form.errors.items %}
|
||||
{% for error in errors %}
|
||||
{{ error }}<br>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<button type="submit" class="black-button">Sign In</button>
|
||||
<div class="ll">
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends "closedverse_main/layout.html" %}
|
||||
{% block main-body %}{% load closedverse_user %}{% load closedverse_tags %}
|
||||
{% user_sidebar request user profile 9 False %}
|
||||
<div class="main-column"><div class="post-list-outline">
|
||||
<h2 class="label">Ban {{ user.username }}</h2>
|
||||
<form class="setting-form" method="post" action={% url "main:user-tools-bans" user.username %}>
|
||||
{% if not banned %}
|
||||
<li class="setting">
|
||||
<p class="settings-label">Reasoning:</p>
|
||||
{{ form.reason }}
|
||||
<p class="settings-label">Expiry:</p>
|
||||
{{ form.expiry_date }}
|
||||
<p class='note'>{{ user.username }} will be banned from {{ brand_name }} until the time is right.</p>
|
||||
</li>
|
||||
{% csrf_token %}
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="button apply-button" value="Ban {{ user.username }}">
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="label">This user is banned already.</p>
|
||||
<p>The ban will expire on: {{ active_ban.expiry_date }}</p>
|
||||
<p>Time remaining: {{ active_ban.expiry_date|timeuntil }}</p>
|
||||
{% endif %}
|
||||
</form></div></div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,49 @@
|
||||
{% extends "closedverse_main/layout.html" %}
|
||||
{% block main-body %}{% load closedverse_user %}{% load closedverse_tags %}
|
||||
{% user_sidebar request user profile 8 False %}
|
||||
<div class="main-column"><div class="post-list-outline">
|
||||
<h2 class="label">Send a warning to {{ user.username }}</h2>
|
||||
<form class="setting-form" method="post" action={% url "main:user-tools-warnings" user.username %}>
|
||||
<li class="setting">
|
||||
<p class="settings-label">Reasoning:</p>
|
||||
{{ form.reason }}
|
||||
<p class='note'>Warnings will show as notifications. {{ user.username }} will not be able to post, comment, or message another user until this notification is seen. {{ user.username }} will not see who sent this warning.</p>
|
||||
</li>
|
||||
<div class="user-data">
|
||||
{% if unread_warnings %}<li>
|
||||
<p class='setting'>Warnings that have not been read yet:<p>
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Incident</th>
|
||||
</tr>
|
||||
{% for Notifications in unread_warnings %}
|
||||
<tr>
|
||||
<td>{{ Notifications.created }}</td>
|
||||
<td>{{ Notifications.context_warning.reason }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>{% endif %}
|
||||
</li>{% if all_warnings %}
|
||||
<p class='setting'>Incident history:<p>
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Issued by</th>
|
||||
<th>Incident</th>
|
||||
</tr>
|
||||
{% for Warning in all_warnings %}
|
||||
<tr>
|
||||
<td>{{ Warning.created }}</td>
|
||||
<td>{{ Warning.by }}</td>
|
||||
<td>{{ Warning.reason }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>{% endif %}
|
||||
</div>
|
||||
{% csrf_token %}
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="button apply-button" value="Send warning to {{ user.username }}">
|
||||
</div>
|
||||
</form></div></div>
|
||||
{% endblock %}
|
||||
@@ -1,12 +1,9 @@
|
||||
{% extends "closedverse_main/layout.html" %}
|
||||
{% block main-body %}{% load closedverse_user %}{% load closedverse_tags %}
|
||||
{% user_sidebar request user profile 7 False %}
|
||||
<div class="main-column"><div class="post-list-outline">
|
||||
<h2 class="label">Change settings for {{ user.username }}</h2>
|
||||
<form class="setting-form" method="post" action={% url "main:user-tools-set" user.username %}>
|
||||
<p>Yes, this form is now ugly, but it's way more powerful.</p>
|
||||
<li class="setting">
|
||||
{% user_sidebar_info user %}
|
||||
|
||||
{% for field in user_form %}
|
||||
<li class='setting'>
|
||||
{% if field.field.widget.input_type == 'checkbox' %}
|
||||
@@ -38,8 +35,6 @@
|
||||
<p class='note'>{{ field.help_text }}</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
</li>
|
||||
<li class="setting">
|
||||
<p class="settings-label">Purge content:</p>
|
||||
{% for field in purge_form %}
|
||||
@@ -51,7 +46,6 @@
|
||||
<div class="center center-input">
|
||||
<p class="settings-label">Metadata:</p>
|
||||
<div class="user-data">
|
||||
<p>User agent: <span>{% if user.user_agent %}{{ user.user_agent }}{% else %}Data missing{% endif %}
|
||||
<p>Rank: <span>{% if user.staff %}Staff member.{% elif not user.level <= 0 %}Moderator. (Level {{ user.level }}){% else %}Regular user.{% endif %}
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<h3>General account information:</h3>
|
||||
<p>IP address: <span>{% if user.addr %}{{ user.addr }}{% else %}Data missing{% endif %}
|
||||
<p>Signup IP address: <span>{% if user.signup_addr %}{{ user.signup_addr }}{% else %}Data missing{% endif %}</p>
|
||||
<p>User agent: <span>{% if user.user_agent %}{{ user.user_agent }}{% else %}Data missing{% endif %}
|
||||
<p>Email: <span>{% if user.email %}{{ user.email }}{% else %}Data missing{% endif %}
|
||||
<p>Rank: <span>{% if user.staff %}Staff member.{% elif not user.level <= 0 %}Moderator. (Level {{ user.level }}){% else %}Regular user.{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<li class="setting-website">
|
||||
<p class="settings-label">Discord Username</p>
|
||||
<div class="center center-input">
|
||||
<input type="text" name="external" maxlength="255" placeholder="Discord Tag" value="{{ profile.external }}">
|
||||
<input type="text" name="external" maxlength="255" placeholder="@" value="{{ profile.external }}">
|
||||
</div>
|
||||
<p class="note">Actually, you don't have to put a Discord username here, you can put anything here, such as your Threads username or X username. Discord sure is popular though.</p>
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -41,6 +41,8 @@ urlpatterns = [
|
||||
url(r'users/'+ username +'/tools/set$', views.user_tools_set, name='user-tools-set'),
|
||||
url(r'users/'+ username +'/tools$', views.user_tools, name='user-tools'),
|
||||
url(r'users/'+ username +'/tools/meta$', views.user_tools_meta, name='user-tools-meta'),
|
||||
url(r'users/'+ username +'/tools/warn$', views.user_tools_warnings, name='user-tools-warnings'),
|
||||
url(r'users/'+ username +'/tools/ban$', views.user_tools_bams, name='user-tools-bans'),
|
||||
url(r'users/'+ username +'/block$', views.user_addblock, name='user-addblock'),
|
||||
url(r'users/'+ username +'/block_rm$', views.user_rmblock, name='user-rmblock'),
|
||||
url(r'my_blacklist$', views.user_blocklist, name='block-list'),
|
||||
@@ -100,7 +102,6 @@ urlpatterns = [
|
||||
url(r'invite/create$', views.create_invite, name='create-invite'),
|
||||
url(r'mydata$', views.my_data, name='my-data'),
|
||||
url(r'changepassword$', views.change_password, name='change-password'),
|
||||
url(r'changepassword/set$', views.change_password_set, name='change-password-set'),
|
||||
url(r'server$', views.server_stat, name='server-stat'),
|
||||
url(r'help/rules/?$', views.help_rules, name='help-rules'),
|
||||
url(r'help/faq/?$', views.help_faq, name='help-faq'),
|
||||
|
||||
@@ -105,7 +105,10 @@ def video_upload(video):
|
||||
return settings.MEDIA_URL + fname
|
||||
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
def image_upload(img, stream=False, drawing=False, avatar=False):
|
||||
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:
|
||||
@@ -137,7 +140,8 @@ def image_upload(img, stream=False, drawing=False, avatar=False):
|
||||
}
|
||||
if orientation in rotations:
|
||||
im = im.transpose(rotations[orientation])
|
||||
if avatar:
|
||||
if avatar or icon:
|
||||
tiny = True
|
||||
# crop 1:1
|
||||
width, height = im.size
|
||||
min_dimension = min(width, height)
|
||||
@@ -149,6 +153,7 @@ def image_upload(img, stream=False, drawing=False, avatar=False):
|
||||
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
|
||||
@@ -174,11 +179,20 @@ def image_upload(img, stream=False, drawing=False, avatar=False):
|
||||
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):
|
||||
|
||||
@@ -13,6 +13,7 @@ from django.contrib.auth import update_session_auth_hash
|
||||
from django.db.models import Q, Count, Exists, OuterRef
|
||||
from django.db.models.functions import Now
|
||||
from .models import *
|
||||
from .forms import *
|
||||
from .util import *
|
||||
from closedverse import settings
|
||||
import re
|
||||
@@ -183,69 +184,33 @@ def community_favorites(request):
|
||||
})
|
||||
|
||||
def login_page(request):
|
||||
"""Login page! using our own user objects."""
|
||||
# Redirect the user to / if they're logged in, forcing them to log out
|
||||
location = '/'
|
||||
# rn the password form does not take into account if you have the old password format or not.
|
||||
if request.user.is_authenticated:
|
||||
return redirect(location)
|
||||
return redirect('/')
|
||||
if request.method == 'POST':
|
||||
# If we don't have all of the POST parameters we want..
|
||||
if not (request.POST['username'] and request.POST['password']):
|
||||
# then return that.
|
||||
return HttpResponseBadRequest("You didn't fill in all of the fields.")
|
||||
# Now let's authenticate.
|
||||
# Wait, first check if the user exists. Remove spaces from the username, because some people do that.
|
||||
# Hold up, first we need to check proxe.
|
||||
|
||||
# Uncomment this if you want users to be forbidden from logging in with a proxy.
|
||||
#if settings.CLOSEDVERSE_PROD and settings.DISALLOW_PROXY and iphub(request.META['REMOTE_ADDR']):
|
||||
# return HttpResponseNotFound("The user doesn't exist.")
|
||||
user = User.objects.authenticate(username=request.POST['username'], password=request.POST['password'])
|
||||
# None = doesn't exist, False = invalid password.
|
||||
if user is None:
|
||||
return HttpResponseNotFound("The user doesn't exist.")
|
||||
else:
|
||||
# Todo: I might want to do some things relating to making models take care of object stuff like this instead of the view, it's totally messed up now.
|
||||
successful = False if user[1] is False or not user[0].is_active() else True
|
||||
LoginAttempt.objects.create(user=user[0], success=successful, user_agent=request.META.get('HTTP_USER_AGENT'), addr=request.META.get('REMOTE_ADDR'))
|
||||
if user[1] == False:
|
||||
# check if a hasher could not be identified due to moving from passlib
|
||||
try:
|
||||
# will throw a ValueError if no hasher is found
|
||||
identify_hasher(user[0].password)
|
||||
except ValueError as e:
|
||||
# error says "Unknown password hashing algorithm ''......", meaning that the password is not converted
|
||||
if '\'\'' in str(e):
|
||||
return HttpResponseBadRequest("The password is either encoded in an unsupported format, or the passwords haven't been updated yet. As part of a recent update, passwords need to be converted - sorry about that. If password resets work, you can use that to make your account usable immediately.")
|
||||
else:
|
||||
return HttpResponseBadRequest("The password is either encoded in an unsupported format, or the settings haven't been updated yet. Please add - or direct the server owner to add - hashers_passlib.bcrypt_sha256 to settings.PASSWORD_HASHERS, and then install django-hashers-passlib. I'm sorry for the inconvenience! If password resets work, you can use that immediately.")
|
||||
else:
|
||||
return HttpResponse("Invalid password.", status=401)
|
||||
elif user[1] == 2:
|
||||
return HttpResponse("This account's password needs to be reset. Contact an admin or reset by email.", status=400)
|
||||
elif not user[0].is_active():
|
||||
return HttpResponseForbidden("This account was disabled.")
|
||||
request.session['passwd'] = user[0].password
|
||||
login(request, user[0])
|
||||
|
||||
# Then, let's get the referrer and either return that or the root.
|
||||
# Actually, let's not for now.
|
||||
#if request.META['HTTP_REFERER'] and "login" not in request.META['HTTP_REFERER'] and request.META['HTTP_HOST'] in request.META['HTTP_REFERER']:
|
||||
# location = request.META['HTTP_REFERER']
|
||||
#else:
|
||||
if request.GET.get('next'):
|
||||
location = request.GET['next']
|
||||
incorrect_password = False
|
||||
form = LoginForm(request.POST)
|
||||
if form.is_valid():
|
||||
user = User.objects.get(username__iexact=form.cleaned_data['username'])
|
||||
# no longer logs failed logins
|
||||
# do I really want to fix that?
|
||||
LoginAttempt.objects.create(user=user,success=True, user_agent=request.META.get('HTTP_USER_AGENT'),addr=request.META.get('REMOTE_ADDR'))
|
||||
request.session['passwd'] = user.password
|
||||
login(request, user)
|
||||
location = request.GET.get('next', '/')
|
||||
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
|
||||
# because if you respond to an ajax with a redirect, the response will just be the page, you can't get the redirect value from js
|
||||
return HttpResponse(location)
|
||||
return redirect(location)
|
||||
else:
|
||||
form = LoginForm()
|
||||
|
||||
return render(request, 'closedverse_main/login_page.html', {
|
||||
'title': 'Log in',
|
||||
'form': form,
|
||||
'allow_signups': settings.allow_signups,
|
||||
'reset_supported': hasattr(settings, 'DEFAULT_FROM_EMAIL'),
|
||||
#'classes': ['no-login-btn']
|
||||
})
|
||||
|
||||
def signup_page(request):
|
||||
"""Signup page, lots of checks here"""
|
||||
# Redirect the user to / if they're logged in, forcing them to log out
|
||||
@@ -280,7 +245,6 @@ def signup_page(request):
|
||||
|
||||
if not re.compile(r'^[A-Za-z0-9-._]{1,32}$').match(request.POST['username']) or not re.compile(r'[A-Za-z0-9]').match(request.POST['username']):
|
||||
return HttpResponseBadRequest("Your username either contains invalid characters or is too long (only letters + numbers, dashes, dots and underscores are allowed")
|
||||
|
||||
# forbidden keywords
|
||||
groups = [
|
||||
[
|
||||
@@ -305,15 +269,6 @@ def signup_page(request):
|
||||
if keyword in request.POST['username'].lower() or keyword in request.POST['nickname'].lower():
|
||||
# perhaps warn admins at a later time
|
||||
return HttpResponseForbidden(messages[id])
|
||||
|
||||
# this is such a miniscule amount of NNIDs that either an admin or a person could
|
||||
# claim all of them, in which case they could not be reused
|
||||
#for keyword in ['windowscj', 'gabalt']:
|
||||
# if keyword in request.POST['origin_id'].lower():
|
||||
# return HttpResponseForbidden("You're very funny. Unfortunately your funniness blah blah blah fuck off.")
|
||||
|
||||
# end of forbidden keywords
|
||||
|
||||
conflicting_user = User.objects.filter(Q(username__iexact=request.POST['username']) | Q(username__iexact=request.POST['username'].replace(' ', '')))
|
||||
if conflicting_user:
|
||||
return HttpResponseBadRequest("A user with that username already exists.")
|
||||
@@ -369,7 +324,7 @@ def signup_page(request):
|
||||
nick = request.POST['nickname']
|
||||
mii = None
|
||||
gravatar = True
|
||||
make = User.objects.closed_create_user(username=request.POST['username'], password=request.POST['password'], email=request.POST.get('email'), addr=request.META['REMOTE_ADDR'], user_agent=request.META['HTTP_USER_AGENT'], signup_addr=request.META['REMOTE_ADDR'], nick=nick, nn=mii, gravatar=gravatar)
|
||||
make = User.objects.closed_create_user(username=request.POST['username'], password=request.POST['password'], email=request.POST.get('email'), addr=request.META['REMOTE_ADDR'], signup_addr=request.META['REMOTE_ADDR'], nick=nick, nn=mii, gravatar=gravatar)
|
||||
if invited == True:
|
||||
invite.used = True
|
||||
invite.used_by = make
|
||||
@@ -400,19 +355,11 @@ def forgot_passwd(request):
|
||||
try:
|
||||
user = User.objects.get(email=request.POST['email'])
|
||||
except (User.DoesNotExist, ValueError):
|
||||
<<<<<<< Updated upstream
|
||||
return HttpResponseNotFound("There isn't a user with that email address.")
|
||||
try:
|
||||
user.password_reset_email(request)
|
||||
except:
|
||||
return HttpResponseBadRequest("There was an error submitting that.")
|
||||
=======
|
||||
return HttpResponseNotFound("The email address could not be found.")
|
||||
try:
|
||||
user.password_reset_email(request)
|
||||
except Exception as error:
|
||||
return HttpResponseBadRequest("There was an error submitting that, sorry! Here's why: " + str(error))
|
||||
>>>>>>> Stashed changes
|
||||
return HttpResponse("Success! Check your emails, it should have been sent from \"{0}\".".format(settings.DEFAULT_FROM_EMAIL))
|
||||
if request.GET.get('token'):
|
||||
user = User.get_from_passwd(request.GET['token'])
|
||||
@@ -488,7 +435,7 @@ def user_view(request, username):
|
||||
nickname_old = user.nickname
|
||||
if profile.cannot_edit:
|
||||
return json_response("Not allowed.")
|
||||
if request.POST.get('screen_name') is None or len(request.POST['screen_name']) > 32:
|
||||
if len(request.POST.get('screen_name')) is 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)')
|
||||
@@ -1044,6 +991,7 @@ def community_favorite_rm(request, community):
|
||||
|
||||
def community_tools(request, community):
|
||||
the_community = get_object_or_404(Community, id=community)
|
||||
activity_feed = True if the_community.type == 4 else False
|
||||
if not request.user.is_authenticated:
|
||||
raise Http404()
|
||||
can_edit = the_community.can_edit_community(request)
|
||||
@@ -1052,77 +1000,17 @@ def community_tools(request, community):
|
||||
return render(request, 'closedverse_main/community_tools.html', {
|
||||
'title': 'Community tools',
|
||||
'community': the_community,
|
||||
'max_icon_size': settings.max_icon_size,
|
||||
'max_banner_size': settings.max_banner_size,
|
||||
'activity_feed': activity_feed,
|
||||
})
|
||||
|
||||
def community_tools_set(request, community):
|
||||
if request.method == 'POST':
|
||||
the_community = get_object_or_404(Community, id=community)
|
||||
#do the checks
|
||||
if not request.user.is_authenticated:
|
||||
return HttpResponseForbidden()
|
||||
can_edit = the_community.can_edit_community(request)
|
||||
if not can_edit:
|
||||
return HttpResponseForbidden()
|
||||
<<<<<<< Updated upstream
|
||||
if len(request.POST.get('community_name')) == 0 or len(request.POST.get('community_name')) >= 100:
|
||||
return json_response('Your community name is either too short or too long.')
|
||||
if len(request.POST.get('community_description')) >= 1024:
|
||||
return json_response('Your community description is too long.')
|
||||
if int(request.POST.get('community_platform')) >= 8:
|
||||
return json_response('Invalid Platform type.')
|
||||
if the_community.is_rm:
|
||||
return json_response('Community is removed')
|
||||
if the_community.type == 4:
|
||||
return json_response('Community is removed')
|
||||
#do the settings
|
||||
the_community.name = request.POST.get('community_name')
|
||||
the_community.description = request.POST.get('community_description')
|
||||
the_community.platform = (request.POST.get('community_platform') or 0)
|
||||
the_community.require_auth = False if request.POST.get('force_login') is None else True
|
||||
ico = request.FILES.get('community_icon')
|
||||
banner = request.FILES.get('community_banner')
|
||||
#if icon is not set, ignore it
|
||||
if ico != None:
|
||||
#make sure it's an image.
|
||||
if 'image' in ico.content_type:
|
||||
# Set the upload limit in megabytes
|
||||
upload_limit = settings.max_icon_size
|
||||
|
||||
max_size = upload_limit * 1024 * 1024
|
||||
current_size = ico.size
|
||||
max_mb = max_size / 1024 / 1024
|
||||
if current_size > max_size:
|
||||
return json_response('Your icon is too big! Please upload an image under ' + str(max_mb) + 'MB. (' + str(round(current_size / 1024 / 1024, 3)) + 'MB)')
|
||||
the_community.ico = ico
|
||||
else:
|
||||
return json_response('bad image')
|
||||
|
||||
if banner != None:
|
||||
if 'image' in banner.content_type:
|
||||
# Set the upload limit in megabytes
|
||||
upload_limit = settings.max_banner_size
|
||||
max_size = upload_limit * 1024 * 1024
|
||||
current_size = banner.size
|
||||
max_mb = max_size / 1024 / 1024
|
||||
if current_size > max_size:
|
||||
# i hate this fucking shit
|
||||
return json_response('Your banner is too big! Please upload an image under ' + str(max_mb) + ' MB. (' + str(round(current_size / 1024 / 1024, 3)) + 'MB)')
|
||||
the_community.banner = banner
|
||||
else:
|
||||
return json_response('bad image')
|
||||
|
||||
'''
|
||||
if request.FILES.get('community_icon') != None:
|
||||
if
|
||||
the_community.ico = request.FILES.get('community_icon')
|
||||
if request.FILES.get('community_banner') != None:
|
||||
the_community.banner = request.FILES.get('community_banner')
|
||||
'''
|
||||
the_community.save()
|
||||
#AuditLog.objects.create(type=2, user=user, by=request.user, reasoning=request.POST)
|
||||
=======
|
||||
form = CommunitySettingForm(request.POST, request.FILES, request , instance=the_community)
|
||||
if not form.is_valid():
|
||||
return json_response(form.errors.as_text())
|
||||
@@ -1139,7 +1027,6 @@ def community_tools_set(request, community):
|
||||
community.banner = upload
|
||||
community.save()
|
||||
AuditLog.objects.create(type=4, community=community, user=community.creator, by=request.user)
|
||||
>>>>>>> Stashed changes
|
||||
return HttpResponse()
|
||||
else:
|
||||
raise Http404()
|
||||
@@ -1202,6 +1089,7 @@ def post_create(request, community):
|
||||
10: "No mr white, you can't make a post entirely consistant of spaces",
|
||||
11: "The video you've uploaded is invalid.",
|
||||
12: "Please don't post Zalgo text.",
|
||||
13: "Please check your notifications.",
|
||||
}.get(new_post))
|
||||
# Render correctly whether we're posting to Activity Feed
|
||||
if community.is_activity():
|
||||
@@ -1335,6 +1223,7 @@ def post_comments(request, post):
|
||||
3: "You're making comments too fast, wait a few seconds and try again.",
|
||||
6: "Not allowed.",
|
||||
12: "Please don't post Zalgo text.",
|
||||
13: "Please check your notifications.",
|
||||
}.get(new_post))
|
||||
# Give the notification!
|
||||
if post.is_mine(request.user):
|
||||
@@ -1670,6 +1559,7 @@ def messages_view(request, username):
|
||||
1: "Your message is too long ("+str(len(request.POST['body']))+" characters, 2200 max).",
|
||||
2: "The image you've uploaded is invalid.",
|
||||
3: "Sorry, but you're sending messages too fast.",
|
||||
4: "Please check your notifications.",
|
||||
6: "Not allowed.",
|
||||
}.get(new_post))
|
||||
friendship.update()
|
||||
@@ -1742,18 +1632,19 @@ def prefs(request):
|
||||
arr = [profile.let_yeahnotifs, lights, request.user.hide_online]
|
||||
return JsonResponse(arr, safe=False)
|
||||
|
||||
@login_required
|
||||
def user_tools(request, username):
|
||||
if not request.user.is_authenticated:
|
||||
raise Http404()
|
||||
if not request.user.can_manage():
|
||||
return HttpResponseForbidden()
|
||||
user = get_object_or_404(User, username__iexact=username)
|
||||
profile = user.profile()
|
||||
profile.setup(request)
|
||||
|
||||
# check if the requesting user is allowed to change someone
|
||||
if user.has_authority(request.user):
|
||||
return HttpResponseForbidden()
|
||||
user_form = UserForm(instance=user)
|
||||
profile_form = ProfileForm(instance=profile)
|
||||
user_form = User_tools_Form(instance=user)
|
||||
profile_form = Profile_tools_Form(instance=profile)
|
||||
purge_form = PurgeForm()
|
||||
seen_by = MetaViews.objects.filter(target_user=user).distinct().order_by('-id')[:10]
|
||||
has_seen = MetaViews.objects.filter(from_user=user).distinct().order_by('-id')[:10]
|
||||
@@ -1774,18 +1665,15 @@ def user_tools(request, username):
|
||||
'accountmatch': accountmatch,
|
||||
'min_lvl_metadata_perms': settings.min_lvl_metadata_perms,
|
||||
})
|
||||
|
||||
@login_required
|
||||
def user_tools_meta(request, username):
|
||||
if not request.user.is_authenticated:
|
||||
raise Http404()
|
||||
if not request.user.can_manage():
|
||||
return HttpResponseForbidden()
|
||||
if request.user.level < settings.min_lvl_metadata_perms:
|
||||
if request.user.level < settings.min_lvl_metadata_perms or settings.min_lvl_metadata_perms == 0:
|
||||
return HttpResponseForbidden()
|
||||
user = get_object_or_404(User, username__iexact=username)
|
||||
profile = user.profile()
|
||||
if user.protect_data:
|
||||
return HttpResponseForbidden()
|
||||
profile.setup(request)
|
||||
# check if the requesting user is allowed to view someone
|
||||
if user.has_authority(request.user):
|
||||
return HttpResponseForbidden()
|
||||
@@ -1822,6 +1710,68 @@ def user_tools_meta(request, username):
|
||||
'min_lvl_metadata_perms': settings.min_lvl_metadata_perms,
|
||||
})
|
||||
|
||||
@login_required
|
||||
def user_tools_warnings(request, username):
|
||||
user = get_object_or_404(User, username__iexact=username)
|
||||
profile = user.profile()
|
||||
profile.setup(request)
|
||||
if not request.user.can_manage():
|
||||
return HttpResponseForbidden()
|
||||
if user.has_authority(request.user):
|
||||
return HttpResponseForbidden()
|
||||
if request.method == 'POST':
|
||||
form = Give_warning_form(request.POST)
|
||||
if form.is_valid():
|
||||
warning = form.save(commit=False)
|
||||
warning.to = user
|
||||
warning.by = request.user
|
||||
warning.save()
|
||||
return redirect('main:user-view', user)
|
||||
unread_warnings = Notification.objects.filter(type=5, to=user, read=False)[:3]
|
||||
all_warnings = Warning.objects.filter(to=user)[:8]
|
||||
form = Give_warning_form()
|
||||
return render(request, 'closedverse_main/man/manage_warnings.html', {
|
||||
'user': user,
|
||||
'unread_warnings': unread_warnings,
|
||||
'all_warnings': all_warnings,
|
||||
'profile': profile,
|
||||
'form': form,
|
||||
})
|
||||
|
||||
@login_required
|
||||
def user_tools_bams(request, username):
|
||||
user = get_object_or_404(User, username__iexact=username)
|
||||
profile = user.profile()
|
||||
profile.setup(request)
|
||||
active_bans = user.banned_user.filter(active=True).exclude(expiry_date__lte=timezone.now())
|
||||
if active_bans:
|
||||
banned = True
|
||||
active_ban = active_bans.first()
|
||||
else:
|
||||
banned = False
|
||||
active_ban = None
|
||||
if not request.user.can_manage():
|
||||
return HttpResponseForbidden()
|
||||
if user.has_authority(request.user):
|
||||
return HttpResponseForbidden()
|
||||
if request.method == 'POST':
|
||||
form = Give_Ban_Form(request.POST)
|
||||
if form.is_valid():
|
||||
ban = form.save(commit=False)
|
||||
ban.to = user
|
||||
ban.by = request.user
|
||||
ban.save()
|
||||
return redirect('main:user-view', user)
|
||||
if not banned:
|
||||
form = Give_Ban_Form()
|
||||
else: form = None
|
||||
return render(request, 'closedverse_main/man/manage_bans.html', {
|
||||
'user': user,
|
||||
'banned': banned,
|
||||
'active_ban': active_ban,
|
||||
'profile': profile,
|
||||
'form': form,
|
||||
})
|
||||
|
||||
def user_tools_set(request, username):
|
||||
if request.method == 'POST':
|
||||
@@ -1835,8 +1785,8 @@ def user_tools_set(request, username):
|
||||
if user.has_authority(request.user):
|
||||
return HttpResponseForbidden()
|
||||
|
||||
user_form = UserForm(request.POST, instance=user)
|
||||
profile_form = ProfileForm(request.POST, instance=profile)
|
||||
user_form = User_tools_Form(request.POST, instance=user)
|
||||
profile_form = Profile_tools_Form(request.POST, instance=profile)
|
||||
purge_form = PurgeForm(request.POST)
|
||||
|
||||
if purge_form.is_valid():
|
||||
@@ -1967,50 +1917,26 @@ def my_data(request):
|
||||
})
|
||||
@login_required
|
||||
def change_password(request):
|
||||
if not request.user.is_authenticated:
|
||||
raise Http404()
|
||||
user = request.user
|
||||
return render(request, 'closedverse_main/change-password.html', {
|
||||
'user': user,
|
||||
'title': 'Change Password',
|
||||
})
|
||||
def change_password_set(request):
|
||||
if request.method == 'POST':
|
||||
if not request.user.is_authenticated:
|
||||
raise Http404()
|
||||
user = request.user
|
||||
old = request.POST.get('old-password')
|
||||
new = request.POST.get('new-password')
|
||||
confirm = request.POST.get('confirm-password')
|
||||
# make sure they are filled out
|
||||
# a bit inefficient but who cares?
|
||||
if not old:
|
||||
return json_response('Please specify your old password.')
|
||||
if not new:
|
||||
return json_response('Please specify your new password.')
|
||||
if not confirm:
|
||||
return json_response('Please specify your new password again.')
|
||||
if not new == confirm:
|
||||
return json_response('Passwords do not match')
|
||||
if old == new:
|
||||
return json_response('The old password and new password can\'t be the same.')
|
||||
form = Settomgs_Change_Password(request.POST)
|
||||
if form.is_valid():
|
||||
old = form.cleaned_data.get('Old_Password')
|
||||
new = form.cleaned_data.get('New_Password')
|
||||
if not user.check_password(old):
|
||||
return json_response('The old password specified does not match the user\'s password. Enter the password you use as of right now.')
|
||||
# do the length check
|
||||
#if len(new) < settings.minimum_password_length:
|
||||
# return json_response('The new password must be at least ' + str(settings.minimum_password_length) + ' characters long.')
|
||||
try:
|
||||
validate_password(new, user=user)
|
||||
except ValidationError as error:
|
||||
return json_response(error)
|
||||
# do the thing
|
||||
return json_response('The old password specified does not match the user\'s password. Enter the password you use as of right now.', "Invalid old password")
|
||||
user.set_password(new)
|
||||
user.save()
|
||||
update_session_auth_hash(request, user)
|
||||
return json_response("Success! Now you can log in with your new password!")
|
||||
return json_response("Success! Now you can log in with your new password!", "Finished")
|
||||
return json_response(form.errors.as_text())
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
form = Settomgs_Change_Password(request.POST)
|
||||
return render(request, 'closedverse_main/change-password.html', {
|
||||
'user': user,
|
||||
'form': form,
|
||||
'title': 'Change Password',
|
||||
})
|
||||
def whatads(request):
|
||||
return render(request, 'closedverse_main/help/whatads.html', {'title': 'What are user-generated ads?'})
|
||||
def help_rules(request):
|
||||
|
||||
@@ -144,6 +144,12 @@ input.disabled {
|
||||
.green {
|
||||
color: green !important;
|
||||
}
|
||||
.warning-reason {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.warning-note {
|
||||
margin-top: 6px;
|
||||
}
|
||||
body {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
@@ -1943,6 +1949,9 @@ font-size: 18px;
|
||||
.button-wiiu:hover {
|
||||
background: #1088b6;
|
||||
}
|
||||
.list .bigger {
|
||||
padding: 29px;
|
||||
}
|
||||
.list > li,
|
||||
.list > div,
|
||||
.list > a,
|
||||
@@ -3386,6 +3395,18 @@ border-top: 1px solid var(--theme, #94ff3d);
|
||||
content: "e";
|
||||
color: var(--theme, #5ac800);
|
||||
}
|
||||
.sidebar-setting .sidebar-admin-settings:before {
|
||||
content: "y";
|
||||
color: var(--theme, #5ac800);
|
||||
}
|
||||
.sidebar-setting .sidebar-admin-warnings:before {
|
||||
content: "n";
|
||||
color: var(--theme, #5ac800);
|
||||
}
|
||||
.sidebar-setting .sidebar-admin-bans:before {
|
||||
content: "x";
|
||||
color: var(--theme, #5ac800);
|
||||
}
|
||||
.sidebar-setting .sidebar-menu-replies:before {
|
||||
content: "r";
|
||||
color: var(--theme, #5ac800);
|
||||
|
||||
@@ -2021,7 +2021,9 @@ var Olv = Olv || {};
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
c.on("drop paste", handleFileChange);
|
||||
c.on("drop", handleFileChange);
|
||||
// upon choosing an image, and pasting plain text after, no image would be uploaded for some reason.
|
||||
// yeah i got no fucking clue how to fix this shit.
|
||||
|
||||
c.on("olv:entryform:post:done", function() {
|
||||
imageDimensions.text(b.EntryForm.tempPollutionButImageFormAllowedText);
|
||||
@@ -3221,7 +3223,7 @@ mode_post = 0;
|
||||
b.router.connect("^/users/[^\/]+/favorites$", function(a, c, d) {
|
||||
b.Content.autopagerize(".community-list", d)
|
||||
}),
|
||||
b.router.connect("^/login/$|^/signup/$", function(c, d, e) {
|
||||
b.router.connect("^/signup/$", function(c, d, e) {
|
||||
function lfinish(b) {
|
||||
window.location.href=b
|
||||
//a('body').attr('sess-usern', a('input[name=username]').val())
|
||||
|
||||
|
Before Width: | Height: | Size: 601 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 760 B |
|
Before Width: | Height: | Size: 519 B |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
@@ -4,37 +4,37 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
width="262.66666"
|
||||
height="64"
|
||||
viewBox="0 0 262.66666 64"
|
||||
width="270.35233"
|
||||
height="61.079422"
|
||||
viewBox="0 0 270.35233 61.079422"
|
||||
sodipodi:docname="menu-logo.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="menu-logo.png"
|
||||
inkscape:export-xdpi="170.44423"
|
||||
inkscape:export-ydpi="170.44423"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<linearGradient
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1"><linearGradient
|
||||
id="linearGradient3"
|
||||
inkscape:collect="always"><stop
|
||||
style="stop-color:#fe3939;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3" /><stop
|
||||
style="stop-color:#c72a2a;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4" /></linearGradient><linearGradient
|
||||
id="linearGradient1"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
inkscape:collect="always"><stop
|
||||
style="stop-color:#ff3a3a;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop1" />
|
||||
<stop
|
||||
id="stop1" /><stop
|
||||
style="stop-color:#ad1a1a;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
x="72.546936"
|
||||
y="26.759483"
|
||||
width="260.0376"
|
||||
height="68.768394"
|
||||
id="rect55" />
|
||||
<linearGradient
|
||||
id="stop2" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1"
|
||||
id="linearGradient2"
|
||||
@@ -42,9 +42,31 @@
|
||||
y1="36.274925"
|
||||
x2="136.69847"
|
||||
y2="70.450951"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3"
|
||||
id="linearGradient4"
|
||||
x1="149.797"
|
||||
y1="0"
|
||||
x2="149.797"
|
||||
y2="325.388"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3"
|
||||
id="linearGradient5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="149.797"
|
||||
y1="0"
|
||||
x2="149.797"
|
||||
y2="325.388" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3"
|
||||
id="linearGradient6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="149.797"
|
||||
y1="0"
|
||||
x2="149.797"
|
||||
y2="325.388" /></defs><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
@@ -61,38 +83,33 @@
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g1" />
|
||||
<g
|
||||
inkscape:current-layer="g1" /><g
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Image"
|
||||
id="g1">
|
||||
<g
|
||||
id="g56">
|
||||
<rect
|
||||
id="g1"
|
||||
transform="translate(-0.78294009,-1.5827909)"><g
|
||||
id="g56"><rect
|
||||
style="fill:#ff4d4d;fill-opacity:1"
|
||||
id="rect53"
|
||||
width="5.9472828"
|
||||
height="5.9472828"
|
||||
x="8.667901"
|
||||
y="16.853588"
|
||||
transform="rotate(-45)" />
|
||||
<rect
|
||||
transform="rotate(-45)" /><rect
|
||||
style="fill:#ad1a1a;fill-opacity:1"
|
||||
id="rect53-3"
|
||||
width="5.9472828"
|
||||
height="7.947773"
|
||||
x="8.667901"
|
||||
y="22.800871"
|
||||
transform="rotate(-45)" />
|
||||
<rect
|
||||
transform="rotate(-45)" /><rect
|
||||
style="fill:#ff7676;fill-opacity:1"
|
||||
id="rect53-3-6"
|
||||
width="5.9472828"
|
||||
height="7.947773"
|
||||
x="16.853588"
|
||||
y="-8.667901"
|
||||
transform="rotate(45)" />
|
||||
<rect
|
||||
transform="rotate(45)" /><rect
|
||||
style="fill:#ff7676;fill-opacity:1"
|
||||
id="rect53-3-6-5"
|
||||
width="7.9418755"
|
||||
@@ -100,8 +117,7 @@
|
||||
x="22.803822"
|
||||
y="-0.72012806"
|
||||
transform="rotate(45)"
|
||||
ry="0" />
|
||||
<rect
|
||||
ry="0" /><rect
|
||||
style="fill:#ff7676;fill-opacity:1"
|
||||
id="rect53-3-6-5-8"
|
||||
width="13.31431"
|
||||
@@ -109,8 +125,7 @@
|
||||
x="30.748648"
|
||||
y="12.59123"
|
||||
transform="rotate(45)"
|
||||
ry="0" />
|
||||
<rect
|
||||
ry="0" /><rect
|
||||
style="fill:#ad1a1a;fill-opacity:1"
|
||||
id="rect53-3-6-5-8-6"
|
||||
width="13.31431"
|
||||
@@ -118,8 +133,7 @@
|
||||
x="-12.591232"
|
||||
y="44.062958"
|
||||
transform="rotate(-45)"
|
||||
ry="0" />
|
||||
<rect
|
||||
ry="0" /><rect
|
||||
style="fill:#ad1a1a;fill-opacity:1"
|
||||
id="rect53-3-6-5-9"
|
||||
width="7.9418755"
|
||||
@@ -127,44 +141,44 @@
|
||||
x="0.72307706"
|
||||
y="30.748646"
|
||||
transform="rotate(-45)"
|
||||
ry="0" />
|
||||
<path
|
||||
ry="0" /><path
|
||||
id="rect54"
|
||||
style="fill:#ff4d4d;fill-opacity:1"
|
||||
transform="rotate(-45)"
|
||||
d="m 0.72012806,22.800871 7.94777294,0 -7.94777294,7.947775 z"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
d="M 0.72012806,22.800871 H 8.667901 l -7.94777294,7.947775 z"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
id="rect54-2"
|
||||
style="fill:#d13434;fill-opacity:1"
|
||||
d="m 27.871707,15.613444 -5.619924,5.619924 -2e-6,-11.2398501 z"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
id="rect54-29"
|
||||
style="fill:#ff4d4d;stroke-width:1.67523;fill-opacity:1"
|
||||
style="fill:#ff4d4d;fill-opacity:1;stroke-width:1.67523"
|
||||
d="m 12.839233,30.645922 9.414637,-9.414637 3e-6,18.829278 z"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
id="rect54-2-3"
|
||||
style="fill:#d13434;stroke-width:1.67523;fill-opacity:1"
|
||||
style="fill:#d13434;fill-opacity:1;stroke-width:1.67523"
|
||||
d="m 31.668508,30.645922 -9.414637,9.414639 -3e-6,-18.829278 z"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:#b68147;fill-opacity:1"
|
||||
d="m 22.253868,40.060561 -6.028143,6.028146 v 12.674516 l 6.028143,3.898989 z"
|
||||
id="path55"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#5e4125;fill-opacity:1"
|
||||
d="m 22.253873,40.060562 6.028143,6.028146 v 12.674516 l -6.028143,3.898989 z"
|
||||
id="path55-2"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc" /></g><path
|
||||
style="font-weight:bold;font-size:42.6667px;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';white-space:pre;shape-padding:4.21878;fill:url(#linearGradient2)"
|
||||
d="m 95.965639,70.450952 q -3.456002,0 -6.485338,-1.280001 -2.986669,-1.322668 -5.248004,-3.62667 -2.218668,-2.346668 -3.498669,-5.461337 -1.237334,-3.114669 -1.237334,-6.741338 0,-3.584003 1.237334,-6.656005 1.280001,-3.114669 3.498669,-5.418671 2.261335,-2.346669 5.248004,-3.669336 2.986669,-1.322668 6.485338,-1.322668 3.285336,0 5.632001,0.853334 2.38934,0.853334 4.77867,2.816002 0.34134,0.256 0.512,0.554667 0.21334,0.256 0.256,0.554667 0.0853,0.256001 0.0853,0.640001 0,0.810667 -0.59734,1.365334 -0.55466,0.512001 -1.36533,0.597334 -0.81067,0.04267 -1.57867,-0.554667 -1.57867,-1.365334 -3.28533,-2.090668 -1.664005,-0.725334 -4.437341,-0.725334 -2.517335,0 -4.736003,1.024001 -2.218669,1.024 -3.925336,2.816002 -1.664002,1.792001 -2.602669,4.181336 -0.938667,2.346669 -0.938667,5.034671 0,2.730668 0.938667,5.120003 0.938667,2.346669 2.602669,4.13867 1.706667,1.792001 3.925336,2.816002 2.218668,0.981334 4.736003,0.981334 2.176002,0 4.096001,-0.725334 1.96267,-0.768 3.75467,-2.090668 0.768,-0.554667 1.49334,-0.469334 0.768,0.04267 1.28,0.597334 0.512,0.512001 0.512,1.450668 0,0.426667 -0.17067,0.853334 -0.17067,0.384 -0.512,0.725334 -2.304,1.834668 -4.94934,2.773335 -2.602665,0.938668 -5.504001,0.938668 z M 123.0163,70.194951 q -3.54133,0 -6.31467,-1.493334 -2.73067,-1.536001 -4.30933,-4.181336 -1.536,-2.688002 -1.536,-6.144005 0,-3.498669 1.45066,-6.144005 1.49334,-2.688002 4.09601,-4.181336 2.60267,-1.536001 5.97333,-1.536001 3.32801,0 5.71734,1.493334 2.38934,1.450668 3.62667,4.053337 1.28,2.560001 1.28,5.930671 0,0.810667 -0.55466,1.365334 -0.55467,0.512 -1.40801,0.512 h -17.19468 v -3.413335 h 17.06668 l -1.74933,1.194667 q -0.0427,-2.133335 -0.85333,-3.797336 -0.81067,-1.706668 -2.30401,-2.688002 -1.49333,-0.981334 -3.62667,-0.981334 -2.432,0 -4.18133,1.066667 -1.70667,1.066668 -2.60267,2.944002 -0.896,1.834669 -0.896,4.181337 0,2.346668 1.06667,4.181336 1.06666,1.834668 2.944,2.901336 1.87733,1.066667 4.30933,1.066667 1.32267,0 2.68801,-0.469333 1.408,-0.512001 2.26133,-1.152001 0.64,-0.469334 1.36534,-0.469334 0.768,-0.04267 1.32266,0.426667 0.72534,0.640001 0.768,1.408001 0.0427,0.768001 -0.68266,1.322668 -1.45067,1.152001 -3.62667,1.877335 -2.13334,0.725333 -4.09601,0.725333 z m 25.25868,0 q -3.328,0 -6.016,-1.536001 -2.64534,-1.578668 -4.22401,-4.26667 -1.536,-2.688002 -1.536,-6.058671 0,-3.370669 1.408,-6.016004 1.45067,-2.688002 3.92534,-4.224003 2.47467,-1.578668 5.58934,-1.578668 2.51733,0 4.65067,1.066667 2.13333,1.024001 3.584,2.816002 V 38.877595 q 0,-0.981334 0.59733,-1.578668 0.64,-0.597334 1.57867,-0.597334 0.98134,0 1.57867,0.597334 0.59733,0.597334 0.59733,1.578668 v 19.456014 q 0,3.370669 -1.57866,6.058671 -1.53601,2.688002 -4.18134,4.26667 -2.64534,1.536001 -5.97334,1.536001 z m 0,-3.840002 q 2.176,0 3.88267,-1.024001 1.70667,-1.066668 2.688,-2.901336 0.98134,-1.834668 0.98134,-4.096003 0,-2.304001 -0.98134,-4.096003 -0.98133,-1.792001 -2.688,-2.816002 -1.70667,-1.066667 -3.88267,-1.066667 -2.13333,0 -3.88267,1.066667 -1.70667,1.024001 -2.73067,2.816002 -0.98133,1.792002 -0.98133,4.096003 0,2.261335 0.98133,4.096003 1.024,1.834668 2.73067,2.901336 1.74934,1.024001 3.88267,1.024001 z m 29.14132,3.840002 q -3.11467,0 -5.58934,-1.536001 -2.47467,-1.578668 -3.92533,-4.224003 -1.40801,-2.688002 -1.40801,-6.058671 0,-3.370669 1.53601,-6.058671 1.57866,-2.688002 4.224,-4.224003 2.688,-1.578668 6.016,-1.578668 3.32801,0 5.97334,1.578668 2.64534,1.536001 4.18134,4.224003 1.57867,2.688002 1.57867,6.058671 h -1.66401 q 0,3.370669 -1.45066,6.058671 -1.408,2.645335 -3.88267,4.224003 -2.47467,1.536001 -5.58934,1.536001 z m 0.85333,-3.840002 q 2.17601,0 3.88267,-1.024001 1.70667,-1.066668 2.68801,-2.858669 0.98133,-1.834668 0.98133,-4.096003 0,-2.304002 -0.98133,-4.096003 -0.98134,-1.834668 -2.68801,-2.858669 -1.70666,-1.066667 -3.88267,-1.066667 -2.13333,0 -3.88267,1.066667 -1.70666,1.024001 -2.73066,2.858669 -0.98134,1.792001 -0.98134,4.096003 0,2.261335 0.98134,4.096003 1.024,1.792001 2.73066,2.858669 1.74934,1.024001 3.88267,1.024001 z m 9.55734,3.712002 q -0.93866,0 -1.57866,-0.597333 -0.59734,-0.640001 -0.59734,-1.578668 v -6.528005 l 0.81067,-4.52267 3.54134,1.536001 v 9.514674 q 0,0.938667 -0.64,1.578668 -0.59734,0.597333 -1.53601,0.597333 z m 10.75199,-14.634677 q 0,-2.560002 1.23733,-4.565337 1.28,-2.048002 3.41334,-3.242669 2.13333,-1.194668 4.69333,-1.194668 2.56,0 3.79734,0.853334 1.28,0.810668 0.98133,1.962668 -0.128,0.597334 -0.512,0.938668 -0.34133,0.298667 -0.81066,0.384 -0.46934,0.08533 -1.02401,-0.04267 -2.73066,-0.554667 -4.90667,-0.08533 -2.176,0.469334 -3.456,1.749335 -1.23733,1.280001 -1.23733,3.242669 z m 0.0427,14.592011 q -1.024,0 -1.57867,-0.512001 -0.55466,-0.554667 -0.55466,-1.621334 V 48.818935 q 0,-1.024 0.55466,-1.578667 0.55467,-0.554667 1.57867,-0.554667 1.06667,0 1.57867,0.554667 0.55467,0.512 0.55467,1.578667 V 67.89095 q 0,1.024 -0.55467,1.578668 -0.512,0.554667 -1.57867,0.554667 z"
|
||||
id="text55"
|
||||
transform="matrix(1.468878,0,0,1.468878,-67.310013,-46.383647)"
|
||||
aria-label="Cedar" />
|
||||
</g>
|
||||
</svg>
|
||||
aria-label="Cedar" /><g
|
||||
fill="#2ba977"
|
||||
id="g2"
|
||||
transform="matrix(0.07550438,0,0,0.07550438,251.86754,5.3031849)"
|
||||
style="fill:url(#linearGradient4)"><path
|
||||
d="m 114.784,0 h 53.278 v 244.191 c -27.29,5.162 -47.38,7.193 -69.117,7.193 C 33.873,251.316 0,222.245 0,166.412 0,112.617 35.93,77.704 91.608,77.704 c 8.64,0 15.222,0.68 23.176,2.717 z m 1.867,124.427 c -6.24,-2.038 -11.382,-2.717 -17.965,-2.717 -26.947,0 -42.512,16.437 -42.512,45.243 0,28.046 14.88,43.532 42.17,43.532 5.896,0 10.696,-0.332 18.307,-1.351 z"
|
||||
id="path1"
|
||||
style="fill:url(#linearGradient5)" /><path
|
||||
d="m 255.187,84.26 v 122.263 c 0,42.105 -3.154,62.353 -12.411,79.81 -8.64,16.783 -20.022,27.366 -43.541,39.055 l -49.438,-23.297 c 23.519,-10.93 34.901,-20.588 42.17,-35.327 7.61,-15.072 10.01,-32.529 10.01,-78.445 V 84.261 h 53.21 z M 196.608,0 h 53.278 v 54.135 h -53.278 z"
|
||||
id="path2"
|
||||
style="fill:url(#linearGradient6)" /></g></g></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 600 B |
|
Before Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 603 B |
|
Before Width: | Height: | Size: 599 B |
|
Before Width: | Height: | Size: 600 B |