From ca09f3295bee7ff75d3e5c5f47c34a1558ab43c5 Mon Sep 17 00:00:00 2001
From: some weird guy <120821766+Mistake35@users.noreply.github.com>
Date: Wed, 23 Aug 2023 16:47:28 -0700
Subject: [PATCH] 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.
---
README.foreskin | 42 ++-
closedverse/settings.py | 60 +---
closedverse_main/admin.py | 83 +++--
closedverse_main/forms.py | 94 ++++-
closedverse_main/middleware.py | 27 ++
closedverse_main/models.py | 163 +++++----
.../closedverse_main/change-password.html | 19 +-
.../closedverse_main/community_all.html | 14 +-
.../closedverse_main/community_list.html | 16 +-
.../closedverse_main/community_tools.html | 10 +-
.../closedverse_main/community_view.html | 10 -
.../elements/discordapp-spinner.html | 1 +
.../elements/file-button.html | 2 +-
.../closedverse_main/elements/message.html | 2 +-
.../elements/user-notification.html | 8 +-
.../elements/user-sidebar.html | 33 +-
.../closedverse_main/help/my-data.html | 1 -
.../closedverse_main/login_page.html | 12 +-
.../closedverse_main/man/manage_bans.html | 25 ++
.../closedverse_main/man/manage_warnings.html | 49 +++
.../closedverse_main/man/usertools.html | 8 +-
.../closedverse_main/man/usertoolsmeta.html | 1 -
.../closedverse_main/profile-settings.html | 2 +-
closedverse_main/templatetags/__init__.py | 1 +
closedverse_main/urls.py | 3 +-
closedverse_main/util.py | 22 +-
closedverse_main/views.py | 322 +++++++-----------
static/closedverse.css | 21 ++
static/closedverse.js | 6 +-
static/img/administrator.png | Bin 601 -> 0 bytes
static/img/badgedes.png | Bin 1153 -> 0 bytes
static/img/cool.png | Bin 381 -> 0 bytes
static/img/developer.png | Bin 760 -> 0 bytes
static/img/jack.png | Bin 519 -> 0 bytes
static/img/menu-logo-old.png | Bin 12105 -> 0 bytes
static/img/menu-logo.png | Bin 5636 -> 0 bytes
static/img/menu-logo.svg | 138 ++++----
static/img/moderator.png | Bin 600 -> 0 bytes
static/img/open-dev.png | Bin 605 -> 0 bytes
static/img/open-zsdev.png | Bin 3088 -> 0 bytes
static/img/sign-in.png | Bin 6924 -> 5232 bytes
static/img/special.png | Bin 603 -> 0 bytes
static/img/tester.png | Bin 599 -> 0 bytes
static/img/verified.png | Bin 600 -> 0 bytes
44 files changed, 652 insertions(+), 543 deletions(-)
create mode 100644 closedverse_main/templates/closedverse_main/elements/discordapp-spinner.html
create mode 100644 closedverse_main/templates/closedverse_main/man/manage_bans.html
create mode 100644 closedverse_main/templates/closedverse_main/man/manage_warnings.html
delete mode 100644 static/img/administrator.png
delete mode 100644 static/img/badgedes.png
delete mode 100644 static/img/cool.png
delete mode 100644 static/img/developer.png
delete mode 100644 static/img/jack.png
delete mode 100644 static/img/menu-logo-old.png
delete mode 100644 static/img/menu-logo.png
delete mode 100644 static/img/moderator.png
delete mode 100644 static/img/open-dev.png
delete mode 100644 static/img/open-zsdev.png
delete mode 100644 static/img/special.png
delete mode 100644 static/img/tester.png
delete mode 100644 static/img/verified.png
diff --git a/README.foreskin b/README.foreskin
index 6817326..6ab0629 100644
--- a/README.foreskin
+++ b/README.foreskin
@@ -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
-- remove the useless feedback thing. (You can just make a bug reporting community)
+- 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)
\ No newline at end of file
diff --git a/closedverse/settings.py b/closedverse/settings.py
index f6d5b96..6ce42ee 100644
--- a/closedverse/settings.py
+++ b/closedverse/settings.py
@@ -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 = """
Cedar
what
Why is this person rehosting it?
-
im bored
"""
+
im bored
+"""
# This option enables some production-specific pages
# and routines, such as HTTPS scheme redirection and
diff --git a/closedverse_main/admin.py b/closedverse_main/admin.py
index 445bb3e..9dd48d8 100644
--- a/closedverse_main/admin.py
+++ b/closedverse_main/admin.py
@@ -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', )
- raw_id_fields = ('user', 'favorite', )
- list_display = ('id', 'user', 'comment', 'let_freedom', )
+ 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,45 +105,42 @@ 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):
return models.Message.real.get_queryset()
class NotificationAdmin(admin.ModelAdmin):
- raw_id_fields = ('to', 'source', 'context_post', 'context_comment', )
- search_fields = ('unique_id', )
- list_display = ('id', 'to', 'source', 'context_post', 'context_comment', )
+ raw_id_fields = ('to', 'source', 'context_post', 'context_comment',)
+ 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')
+ raw_id_fileds = ('id', 'created', 'url', 'imageurl')
class YeahAdmin(admin.ModelAdmin):
- raw_id_fields = ('by', 'post', 'comment', )
- list_display = ('by', 'post', 'comment', )
- search_fields = ('by__username', 'post__body', 'comment__body', )
+ raw_id_fields = ('by', 'post', 'comment', )
+ list_display = ('by', 'post', 'comment', )
+ search_fields = ('by__username', 'post__body', 'comment__body', )
class HistoryAdmin(admin.ModelAdmin):
- raw_id_fields = ('user',)
- list_display = ('id', 'user')
+ raw_id_fields = ('user',)
+ list_display = ('id', 'user')
class RoleAdmin(admin.ModelAdmin):
- exclude = ('is_static', )
+ exclude = ('is_static', )
#class BlockAdmin(admin.ModelAdmin)
@@ -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
diff --git a/closedverse_main/forms.py b/closedverse_main/forms.py
index 6ccdc3d..e5a4260 100644
--- a/closedverse_main/forms.py
+++ b/closedverse_main/forms.py
@@ -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):
@@ -20,21 +23,62 @@ class CommunitySettingForm(forms.ModelForm):
'community_icon',
'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']
\ No newline at end of file
+ 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']
\ No newline at end of file
diff --git a/closedverse_main/middleware.py b/closedverse_main/middleware.py
index 88a3927..20554b0 100644
--- a/closedverse_main/middleware.py
+++ b/closedverse_main/middleware.py
@@ -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
diff --git a/closedverse_main/models.py b/closedverse_main/models.py
index 6ce19e6..5173edf 100644
--- a/closedverse_main/models.py
+++ b/closedverse_main/models.py
@@ -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)
@@ -616,7 +581,44 @@ class User(AbstractBaseUser):
except:
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)
diff --git a/closedverse_main/templates/closedverse_main/change-password.html b/closedverse_main/templates/closedverse_main/change-password.html
index de81a5e..4885285 100644
--- a/closedverse_main/templates/closedverse_main/change-password.html
+++ b/closedverse_main/templates/closedverse_main/change-password.html
@@ -4,21 +4,12 @@
Change password
-
-
- {% if user.is_warned and user.is_active %}
-
-
WARNING: You have been issued a warning by an administrator.
-
- {% if user.get_warned_reason %}
-
Reason: "{{ user.get_warned_reason }}"
- {% endif %}
-
-
- {% endif %}
{% if not user.is_active and user.is_authenticated %}
Oops: You've been smacked by an admin. Better luck next time.
@@ -44,6 +29,7 @@
{% endif %}
{% if announcements and request.user.is_authenticated %}
+ Skip to communities
Latest Announcements
diff --git a/closedverse_main/templates/closedverse_main/community_tools.html b/closedverse_main/templates/closedverse_main/community_tools.html
index 1594603..1dac816 100644
--- a/closedverse_main/templates/closedverse_main/community_tools.html
+++ b/closedverse_main/templates/closedverse_main/community_tools.html
@@ -20,23 +20,19 @@
{% if request.user.has_freedom %}
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.
{% endif %}
{% time notification.latest %}
{% if notification.type == 4 and not notification.source.is_following and not notification.all_users|length > 1 %}
diff --git a/closedverse_main/templates/closedverse_main/elements/user-sidebar.html b/closedverse_main/templates/closedverse_main/elements/user-sidebar.html
index 559c09c..a4ce615 100644
--- a/closedverse_main/templates/closedverse_main/elements/user-sidebar.html
+++ b/closedverse_main/templates/closedverse_main/elements/user-sidebar.html
@@ -11,17 +11,6 @@
{% endif %}
-
- {% if user.is_warned and user.is_active %}
-
-
Notice: This user has received a warning by an administrator.
-
- {% if user.get_warned_reason %}
-
Reason: "{{ user.get_warned_reason }}"
- {% endif %}
-
-
- {% endif %}
{% if profile.favorite.screenshot %}
@@ -50,13 +39,10 @@
{% endif %}
{% endif %}
- {% if not has_authority and request.user.can_manage %}
-
- {% endif %}
- {% if not general and not user.is_me and profile.can_block %}
+ {% if not user.is_me %}
-
+ {% if profile.can_block %}{% endif %}
{% endif %}
@@ -124,6 +110,21 @@
{% endif %}
+ {% if not has_authority and request.user.can_manage %}
+
{% if profile.comment %}
diff --git a/closedverse_main/templates/closedverse_main/help/my-data.html b/closedverse_main/templates/closedverse_main/help/my-data.html
index cc4abfa..b9d24ff 100644
--- a/closedverse_main/templates/closedverse_main/help/my-data.html
+++ b/closedverse_main/templates/closedverse_main/help/my-data.html
@@ -9,7 +9,6 @@
General account information:
IP address: {% if user.addr %}{{ user.addr }}{% else %}Data missing{% endif %}
Signup IP address: {% if user.signup_addr %}{{ user.signup_addr }}{% else %}Data missing{% endif %}
-
User agent: {% if user.user_agent %}{{ user.user_agent }}{% else %}Data missing{% endif %}
Rank: {% 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 %}
My content:
Your account has existed for {{ age }} days! During that time, we've collected: