diff --git a/README.foreskin b/README.foreskin index cf85ad4..ff587e2 100644 --- a/README.foreskin +++ b/README.foreskin @@ -2,15 +2,27 @@ Stuff that is done: - Disable comments button - merge with closedverse-video-support (mostly) - Prevent users from posting in communities created by blocked users. +<<<<<<< Updated upstream +======= +- 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. +>>>>>>> Stashed changes Stuff that is in progress - Removing shitty code. -- Finding and killing bugs! +- moving to forms.py Stuff that I want - Image and video file boxes in one. - Pinning posts???? Not needed at all if I'm being honest. - Ways for mods to view who invited who. +<<<<<<< Updated upstream - Hide email password reset form if no SMTP server is set up. -- Fix icon and banner uploads. Make that shit work with image_upload \ No newline at end of file +- Fix icon and banner uploads. Make that shit work with image_upload +======= +- Full ImageField integration +- remove the useless feedback thing. (You can just make a bug reporting community) +>>>>>>> Stashed changes diff --git a/closedverse/settings.py b/closedverse/settings.py index ce03a1f..f6d5b96 100644 --- a/closedverse/settings.py +++ b/closedverse/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/2.2/ref/settings/ """ import os +from django.conf.global_settings import PASSWORD_HASHERS # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -129,21 +130,28 @@ LOGGING = { # Password validation # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] +# only use these when debug is not enabled so that making dummy accounts in debug is easier +if not DEBUG: + AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, + ] +# append the old passlib hasher to the end of PASSWORD_HASHERS +# you only need this if your closedverse instance has passwords from before 08/2023, otherwise leave it commented +# although django has a bcrypt sha256 method, it's not compatible with passlib's, which is what closedverse used +# python3 -m pip install django-hashers-passlib +#PASSWORD_HASHERS += ['hashers_passlib.bcrypt_sha256'] # Internationalization # https://docs.djangoproject.com/en/2.2/topics/i18n/ @@ -160,6 +168,14 @@ 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/ @@ -239,6 +255,7 @@ DISALLOW_PROXY = False # Setting this to True forces every user to log in/ # sign up for the site to view any content. FORCE_LOGIN = False + # A list of URLs that are always accessible # whether the above value is set or not. LOGIN_EXEMPT_URLS = { @@ -249,6 +266,7 @@ LOGIN_EXEMPT_URLS = { r'^help/rules$', r'^help/contact$', r'^help/login$', + r'^s/.*$', } # Action to perform on images belonging to posts/ @@ -272,6 +290,7 @@ 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 @@ -280,6 +299,8 @@ max_banner_size = 1 # 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 @@ -297,6 +318,13 @@ age_allowed = "13" # brand name currently being implemented throughout templates brand_name = "Cedar" +memo_title = 'Cedar-Django' +memo_msg = """ +

Cedar

+

what

+

Why is this person rehosting it?

+

im bored

""" + # This option enables some production-specific pages # and routines, such as HTTPS scheme redirection and # proxy detection via IPHub. diff --git a/closedverse_main/admin.py b/closedverse_main/admin.py index 0b0b7e9..445bb3e 100644 --- a/closedverse_main/admin.py +++ b/closedverse_main/admin.py @@ -126,11 +126,6 @@ class AdsAdmin(admin.ModelAdmin): class InvitesAdmin(admin.ModelAdmin): raw_id_fileds = ('id', 'created', 'creator') -class WelcomemsgAdmin(admin.ModelAdmin): - raw_id_fileds = ('id', 'created', 'message', ) - list_display = ('Title', 'message', 'show', 'order', 'created', ) - actions = [Hide_Memo, Show_Memo] - class YeahAdmin(admin.ModelAdmin): raw_id_fields = ('by', 'post', 'comment', ) list_display = ('by', 'post', 'comment', ) @@ -140,10 +135,14 @@ class HistoryAdmin(admin.ModelAdmin): raw_id_fields = ('user',) list_display = ('id', 'user') +class RoleAdmin(admin.ModelAdmin): + exclude = ('is_static', ) + #class BlockAdmin(admin.ModelAdmin) 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.Community, CommunityAdmin) @@ -158,6 +157,7 @@ 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) @@ -167,3 +167,15 @@ admin.site.register(models.Friendship, ConversationAdmin) admin.site.register(models.Invites, InvitesAdmin) admin.site.register(models.Poll) admin.site.register(models.PollVote) +======= + +if settings.DEBUG: + admin.site.register(models.Yeah) + admin.site.register(models.Follow) + admin.site.register(models.FriendRequest) + admin.site.register(models.Friendship, ConversationAdmin) + #admin.site.register(models.Notification) + admin.site.register(models.Poll) + admin.site.register(models.PollVote) +admin.site.register(models.Ads, AdsAdmin) +>>>>>>> Stashed changes diff --git a/closedverse_main/context_processors.py b/closedverse_main/context_processors.py index cd317b4..f3ec51f 100644 --- a/closedverse_main/context_processors.py +++ b/closedverse_main/context_processors.py @@ -5,8 +5,13 @@ except ImportError: from closedverse_main import apps brand_name = apps.ClosedverseMainConfig.verbose_name +# for brand logo +from closedverse.settings import STATIC_URL +# variable for this and name are here for imports +brand_logo = STATIC_URL + 'img/menu-logo.svg' + # the name of the function is merely what's imported into settings.py def brand_name_universal(request): # this returns what's actually newly available to the template # so the name of the key actually dictates what you put in the tmpl - return {"brand_name": brand_name} + return {"brand_name": brand_name, "brand_logo": brand_logo} diff --git a/closedverse_main/forms.py b/closedverse_main/forms.py new file mode 100644 index 0000000..6ccdc3d --- /dev/null +++ b/closedverse_main/forms.py @@ -0,0 +1,52 @@ +from django import forms +from .models import * +from django.core.exceptions import ValidationError + +# 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): + community_name = forms.CharField(max_length=64,required=True) + community_description = forms.CharField(max_length = 2200,required=False) + community_platform = forms.IntegerField(max_value = 7, min_value = 0, required = True) + force_login = forms.BooleanField(required = False) + community_icon = forms.ImageField(required = False) + community_banner = forms.ImageField(required = False) + class Meta: + model = Community + fields = ( + 'community_name', + 'community_description', + 'community_platform', + 'force_login', + 'community_icon', + 'community_banner' + ) + +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): + 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.') + + class Meta: + model = User + fields = ['username', 'nickname', 'role', 'c_tokens', 'hide_online', 'active', 'can_invite', 'warned', 'has_mh', 'avatar', 'warned_reason'] + + def clean_username(self): + username = self.cleaned_data.get('username') + if not re.compile(r'^[A-Za-z0-9-._]{1,32}$').match(username) or not re.compile(r'[A-Za-z0-9]').match(username): + raise forms.ValidationError("The username contains invalid characters.") + return username + + +class ProfileForm(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 diff --git a/closedverse_main/middleware.py b/closedverse_main/middleware.py index 368525b..88a3927 100644 --- a/closedverse_main/middleware.py +++ b/closedverse_main/middleware.py @@ -42,12 +42,6 @@ class ClosedMiddleware(object): # can just forbid post requests for the time being (but leav our funny logout message :3) if not request.user.is_active() and request.method != 'GET' and request.get_full_path() != '/logout/': return HttpResponseForbidden() - # If there isn't a request.session - if not request.session.get('passwd'): - request.session['passwd'] = request.user.password - else: - if request.session['passwd'] != request.user.password: - logout(request) response = self.get_response(request) if request.user.is_authenticated: # for reverse proxy diff --git a/closedverse_main/models.py b/closedverse_main/models.py index 84f1c7b..6ce19e6 100644 --- a/closedverse_main/models.py +++ b/closedverse_main/models.py @@ -1,16 +1,16 @@ from __future__ import unicode_literals from django.db import models +from django.contrib.auth.base_user import AbstractBaseUser from django.contrib.auth.models import BaseUserManager from django.db.models import Q, QuerySet, Max, F, Count, Case, When, Exists, OuterRef, Subquery from django.utils import timezone -from django.forms.models import model_to_dict -from django.utils.dateformat import format from django.core.validators import RegexValidator, URLValidator from django.core.exceptions import ValidationError -from datetime import timedelta, datetime, date, time -from passlib.hash import bcrypt_sha256 +from datetime import timedelta, time +#from passlib.hash import bcrypt_sha256 +# you may want to import django.conf.settings instead (then change checks for DEFAULT_FROM_EMAIL) from closedverse import settings -from closedverse_main.context_processors import brand_name +from closedverse_main.context_processors import brand_name, brand_logo from . import util from random import getrandbits import uuid, json, base64 @@ -18,7 +18,6 @@ from django.core.mail import send_mail from django.template.loader import render_to_string from django.urls import reverse from django.db.models.signals import pre_delete -from django.dispatch import receiver import re import unicodedata import random @@ -120,12 +119,23 @@ class ColorField(models.CharField): def __init__(self, *args, **kwargs): kwargs['max_length'] = 18 super(ColorField, self).__init__(*args, **kwargs) + +# custom role in db +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.') + organization = models.CharField(max_length=255, blank=True, null=True, help_text='Text that shows above one\'s username') + + def __str__(self): + return "role \"" + str(self.organization) + "\"" + #mii_domain = 'https://mii-secure.cdn.nintendo.net' # as of writing, mii-secure is unstable, nintendo please do not f*ck me for this mii_domain = 'https://s3.us-east-1.amazonaws.com/mii-images.account.nintendo.net/' -class User(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) +class User(AbstractBaseUser): id = models.AutoField(primary_key=True) username = models.CharField(max_length=32, unique=True) # Todo: Don't allow nickname to be null (once this is fixed, un-str-ify line 357 of views; the one with ogdata description) @@ -139,13 +149,23 @@ class User(models.Model): # 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'),)) + 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) @@ -189,16 +209,19 @@ class User(models.Model): 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: + # could this be shortened? or discontinued in general return self.profile_set.all().values_list(thing, flat=True).first() # Otherwise just get full profile - return self.profile_set.filter(user=self).first() + return self.profile_set.filter().first() def gravatar(self): g = util.get_gravatar(self.email) if not g: @@ -238,34 +261,29 @@ class User(models.Model): return int(limit) - recent_posts def get_class(self): + """ first = { - 1: 'cool', - 2: 'administrator', - 3: 'moderator', - 4: 'openverse', - 5: 'donator', - 6: 'cool', - 7: 'urapp', - 8: 'developer', - 9: 'badgedes', - 10: 'jack', - 11: 'verifiedd', + 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", - 4: "O-PHP-enverse Man", - 5: "Donator", - 6: "Cool Person", - 7: "cave story is okay", - 8: "owner guy", 9: "Badge Designer", - 10: "stupid man", - 11: "Verified", }.get(self.role, '') - if first: - first = 'official ' + first + """ + #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: + first = settings.STATIC_URL + self.role.image.name return [first, second] def is_me(self, request): if request.user.is_authenticated: @@ -305,17 +323,17 @@ class User(models.Model): return self.avatar def num_yeahs(self): - return self.yeah_set.filter(by=self).count() + return self.yeah_set.count() def num_posts(self): - return self.post_set.filter(creator=self).count() + return self.post_set.count() def num_comments(self): - return self.comment_set.filter().count() + return self.comment_set.count() def num_following(self): - return self.follow_source.filter().count() + return self.follow_source.count() def num_followers(self): - return self.follow_target.filter().count() + return self.follow_target.count() def num_friends(self): - return self.friend_source.filter().count() + self.friend_target.filter().count() + return self.friend_source.count() + self.friend_target.count() def can_follow(self, user): if UserBlock.find_block(self, user): return False @@ -345,23 +363,29 @@ class User(models.Model): return False return self.follow_target.filter(source=source, target=self).delete() def can_block(self, source): - if self.can_manage() or self.level > source.level: + if self.can_manage() or self.level > source.level or self == source: return False #if source.profile('moyenne'): - # return False + # return False return True # BLOCK this user from SOURCE def make_block(self, source): # trailing - block = UserBlock.find_block(self, source) - if block and block.source == source: - return block.delete() + if UserBlock.objects.filter(source=source, target=self).exists(): + return False + if not self.can_block(source): + return False fs = Friendship.find_friendship(self, source) if fs: fs.delete() # delete any mutual follows Follow.objects.filter(Q(source=self) & Q(target=source) | Q(target=self) & Q(source=source)).delete() return UserBlock.objects.create(source=source, target=self) + def remove_block(self, source): + find_block = UserBlock.objects.filter(source=source, target=self) + if not find_block: + return False + return find_block.delete() def get_posts(self, limit, offset, request, offset_time): if request.user.is_authenticated: has_yeah = Yeah.objects.filter(post=OuterRef('id'), by=request.user.id) @@ -454,8 +478,13 @@ class User(models.Model): except: pass def send_fr(self, source, body=None): - if not self.get_fr(source): - return FriendRequest.objects.create(source=source, target=self, body=body) + if self == source or not self.profile().can_friend(source): + return False + if self.get_fr(source): + return False + if Friendship.find_friendship(self, source): + return False + return FriendRequest.objects.create(source=source, target=self, body=body) def accept_fr(self, target): fr = self.get_fr(target) if fr: @@ -517,7 +546,7 @@ class User(models.Model): return self.save(update_fields=['last_login']) def has_postspam(self, body, screenshot=None, drawing=None): - latest_post = self.post_set.filter().order_by('-created')[:1] + latest_post = self.post_set.order_by('-created')[:1] if not latest_post: return False latest_post = latest_post.first() @@ -545,7 +574,7 @@ class User(models.Model): return messages def password_reset_email(self, request): htmlmsg = render_to_string('closedverse_main/help/email.html', { - 'menulogo': request.build_absolute_uri(settings.STATIC_URL + 'img/menu-logo.svg'), + 'menulogo': request.build_absolute_uri(brand_logo), 'contact': request.build_absolute_uri(reverse('main:help-contact')), 'link': request.build_absolute_uri(reverse('main:forgot-passwd')) + "?token=" + base64.urlsafe_b64encode(bytes(self.password, 'utf-8')).decode(), }) @@ -609,7 +638,6 @@ class Invites(models.Model): return "invite by " + str(self.creator) class Community(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) description = models.TextField(blank=True, default='') @@ -673,6 +701,7 @@ class Community(models.Model): 5: 'pc', 6: 'xbox', 7: 'ps', + 8: 'youre-mom', }.get(self.platform) if not thing: return None @@ -827,7 +856,6 @@ class CommunityFavorite(models.Model): return "Community favorite by " + str(self.by) + " for " + str(self.community) class Post(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) community = models.ForeignKey(Community, null=True, on_delete=models.CASCADE) feeling = models.SmallIntegerField(default=0, choices=feelings) @@ -892,13 +920,13 @@ class Post(models.Model): def number_yeahs(self): if hasattr(self, 'num_yeahs'): return self.num_yeahs - return self.yeah_set.filter(post=self).count() + return self.yeah_set.count() def has_yeah(self, request): if request.user.is_authenticated: if hasattr(self, 'yeah_given'): return self.yeah_given else: - return self.yeah_set.filter(post=self, by=request.user).exists() + return self.yeah_set.filter(by=request.user).exists() else: return False def can_yeah(self, request): @@ -926,14 +954,14 @@ class Post(models.Model): def remove_yeah(self, request): if not self.has_yeah(request): return True - return self.yeah_set.filter(post=self, by=request.user).delete() + return self.yeah_set.filter(by=request.user).delete() def number_comments(self): # Number of comments cannot be accurate due to comment deleting #if hasattr(self, 'num_comments'): # return self.num_comments - return self.comment_set.filter(original_post=self).count() + return self.comment_set.count() def get_yeahs(self, request): - return Yeah.objects.filter(type=0, post=self).order_by('-created')[0:30] + return self.yeah_set.order_by('-created')[0:30] def can_comment(self, request): if self.number_comments() > 500: return False @@ -1105,7 +1133,6 @@ class Post(models.Model): return the_post.first() class Comment(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) original_post = models.ForeignKey(Post, on_delete=models.CASCADE) community = models.ForeignKey(Community, on_delete=models.CASCADE) @@ -1144,13 +1171,13 @@ class Comment(models.Model): def number_yeahs(self): if hasattr(self, 'num_yeahs'): return self.num_yeahs - return self.yeah_set.filter(comment=self, type=1).count() + return self.yeah_set.count() def has_yeah(self, request): if request.user.is_authenticated: if hasattr(self, 'yeah_given'): return self.yeah_given else: - return self.yeah_set.filter(comment=self, type=1, by=request.user).exists() + return self.yeah_set.filter(by=request.user).exists() else: return False def can_yeah(self, request): @@ -1175,7 +1202,7 @@ class Comment(models.Model): def remove_yeah(self, request): if not self.has_yeah(request): return True - return self.yeah_set.filter(comment=self, type=1, by=request.user).delete() + return self.yeah_set.filter(by=request.user).delete() def get_yeahs(self, request): return Yeah.objects.filter(type=1, comment=self).order_by('-created')[0:30] def owner_post(self): @@ -1219,7 +1246,7 @@ class Comment(models.Model): self.is_mine = self.is_mine(request.user) class Yeah(models.Model): - # Todo: make this a plain int at some point + # 2023-08-16: tried to remove this but it is the primary key so it's kind of hard to change id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True) by = models.ForeignKey(User, on_delete=models.CASCADE) type = models.SmallIntegerField(default=0, choices=((0, 'post'), (1, 'comment'), )) @@ -1232,14 +1259,13 @@ class Yeah(models.Model): def __str__(self): a = "from " + self.by.username + " to " if self.post: - a += str(self.post.unique_id) + a += "post " + str(self.post.id) elif self.comment: - a += str(self.comment.unique_id) + a += "comment " + str(self.comment.id) return a class Profile(models.Model): is_new = models.BooleanField(default=True) - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) user = models.ForeignKey(User, on_delete=models.CASCADE) @@ -1272,11 +1298,11 @@ class Profile(models.Model): # Post limit, 0 for none limit_post = models.SmallIntegerField(default=0) # If this is true, the user can't change their avatar or nickname - cannot_edit = models.BooleanField(default=False) + cannot_edit = models.BooleanField(default=False, help_text='Make it so this user cannot change settings.') email_login = models.SmallIntegerField(default=1, choices=((0, 'Do not allow'), (1, 'Okay'), (2, 'Only allow'))) def __str__(self): - return "profile " + str(self.unique_id) + " for " + self.user.username + return "profile for " + self.user.username def origin_id_public(self, user=None): if user == self.user: return self.origin_id @@ -1312,12 +1338,12 @@ class Profile(models.Model): def can_friend(self, user=None): if self.let_friendrequest == 2: return False - #if user.is_authenticated and UserBlock.find_block(self.user, user): - # return False elif self.let_friendrequest == 1: if not user.is_following(self.user): return False return True + if user.is_authenticated and UserBlock.find_block(self.user, user): + return False return True def got_fullurl(self): if self.weblink: @@ -1342,7 +1368,6 @@ class Profile(models.Model): class Follow(models.Model): # Todo: remove this - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) source = models.ForeignKey(User, related_name='follow_source', on_delete=models.CASCADE) target = models.ForeignKey(User, related_name='follow_target', on_delete=models.CASCADE) @@ -1461,7 +1486,6 @@ class Notification(models.Model): return user.notification_sender.create(source=user, type=type, to=to, context_post=post, context_comment=comment) class Complaint(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) creator = models.ForeignKey(User, on_delete=models.CASCADE) type = models.SmallIntegerField(choices=( @@ -1480,7 +1504,6 @@ class Complaint(models.Model): return user.complaint_set.filter(created__gt=timezone.now() - timedelta(minutes=5)).exists() class FriendRequest(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) source = models.ForeignKey(User, related_name='fr_source', on_delete=models.CASCADE) target = models.ForeignKey(User, related_name='fr_target', on_delete=models.CASCADE) @@ -1496,7 +1519,6 @@ class FriendRequest(models.Model): self.save() class Friendship(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) source = models.ForeignKey(User, related_name='friend_source', on_delete=models.CASCADE) target = models.ForeignKey(User, related_name='friend_target', on_delete=models.CASCADE) @@ -1552,7 +1574,6 @@ class Friendship(models.Model): return friends class Conversation(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) source = models.ForeignKey(User, related_name='conv_source', on_delete=models.CASCADE) target = models.ForeignKey(User, related_name='conv_target', on_delete=models.CASCADE) @@ -1572,9 +1593,9 @@ class Conversation(models.Model): def set_read(self, user): return self.unread(user).update(read=True) def all_read(self): - return self.message_set.filter().update(read=True) + return self.message_set.update(read=True) def messages(self, request, limit=50, offset=0): - msgs = self.message_set.filter().order_by('-created')[offset:offset + limit] + msgs = self.message_set.order_by('-created')[offset:offset + limit] for msg in msgs: msg.mine = msg.mine(request.user) return msgs @@ -1601,7 +1622,6 @@ class Conversation(models.Model): new_post.mine = True return new_post class Message(models.Model): - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE) feeling = models.SmallIntegerField(default=0, choices=feelings) @@ -1660,8 +1680,6 @@ class ConversationInvite(models.Model): return "Invite to conversation " + str(self.conversation) + " from " + str(self.source) + " to " + str(self.target) class Poll(models.Model): - # Todo: make this a plain int at some point - unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) id = models.AutoField(primary_key=True) able_vote = models.BooleanField(default=True) choices = models.TextField(default="[]") @@ -1811,15 +1829,6 @@ class Ads(models.Model): def __str__(self): return "Ad with id " + str(self.id) + ", created at " + str(self.created) + ", with url " + str(self.url) + ", and imageurl " + str(self.imageurl) - -class welcomemsg(models.Model): - id = models.AutoField(primary_key=True) - order = models.IntegerField(max_length=3, default=1) - show = models.BooleanField(default=True) - created = models.DateTimeField(auto_now_add=True) - Title = models.CharField(max_length=256, null=False, blank=False, default='Title') - message = models.TextField(null=False, blank=False) - image = models.ImageField(upload_to='welcomemsg/%y/%m/%d/', max_length=255, null=True, blank=True) # thing will log changes to your bio or nickname class ProfileHistory(models.Model): diff --git a/closedverse_main/templates/403.html b/closedverse_main/templates/403.html index 53e56a1..a928ca0 100644 --- a/closedverse_main/templates/403.html +++ b/closedverse_main/templates/403.html @@ -1,4 +1,4 @@ {% extends "closedverse_main/layout.html" %} {% load closedverse_tags %}{% block main-body %} -{% nocontent "403 Forbidden" %} -{% endblock %} \ No newline at end of file +{% nocontent "The page could not be displayed." %} +{% endblock %} diff --git a/closedverse_main/templates/closedverse_main/block-list.html b/closedverse_main/templates/closedverse_main/block-list.html new file mode 100644 index 0000000..3476a87 --- /dev/null +++ b/closedverse_main/templates/closedverse_main/block-list.html @@ -0,0 +1,43 @@ +{% extends "closedverse_main/layout.html" %} +{% load static %}{% load closedverse_user %}{% load closedverse_tags %} +{% block main-body %} +{% user_sidebar request user user.profile 0 True %} +
+

Blocked Users

+ {% if blocks %} +
+ {% for block in blocks %} +
  • + {% user_icon_container block.target %} + +
    + {{ block.target.nickname }}
    {% time block.created %} + +
    +
  • +
    +
    +
    +

    Unblock {{ block.target.nickname }}

    +
    + {% user_sidebar_info block.target %} +
    +

    Unblock this user?

    +
    + + +
    +
    +
    +
    +
    +
    + {% endfor %} +
    + {% else %} +
    +

    You have not blocked any users.

    +
    + {% endif %} +
    +{% endblock %} diff --git a/closedverse_main/templates/closedverse_main/comment-view.html b/closedverse_main/templates/closedverse_main/comment-view.html index 8990ae0..c86cbf3 100644 --- a/closedverse_main/templates/closedverse_main/comment-view.html +++ b/closedverse_main/templates/closedverse_main/comment-view.html @@ -10,7 +10,7 @@
    -
    +

    {{ comment.community.name }}

    {% if comment.is_mine or comment.can_rm %} {% if user.is_active %} @@ -85,4 +85,4 @@
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/closedverse_main/templates/closedverse_main/community_list.html b/closedverse_main/templates/closedverse_main/community_list.html index c99ec4b..c03a474 100644 --- a/closedverse_main/templates/closedverse_main/community_list.html +++ b/closedverse_main/templates/closedverse_main/community_list.html @@ -34,6 +34,15 @@
    {% endif %} + {% if settings.memo_title and settings.memo_msg %} +
    +

    {{ settings.memo_title }}

    +
    + {% autoescape off %}{{ settings.memo_msg }}{% endautoescape %} +
    +
    + {% endif %} + {% if announcements and request.user.is_authenticated %}
    @@ -48,17 +57,6 @@
    {% endif %} - - {% if WelcomeMSG and not request.user.is_authenticated %} -
    -

    Welcome to {{ brand_name }}!

    - {% for WelcomeMSG in WelcomeMSG %} -

    {{ WelcomeMSG.Title }}

    -

    {{ WelcomeMSG.message|linebreaksbr|urlize }}

    - {% if WelcomeMSG.image %}{% endif %} - {% endfor %} -
    - {% endif %} {% if request.user.c_tokens > 0 %}Create a community{% endif %} {% if availableads %}
    @@ -97,6 +95,12 @@ {% community_page_element user_communities "Popular User-Created Communities" False "usr" %} {% if user_communities %}Show more{% endif %} {% community_page_element my_communities "My Communities" %} + + {% if not feature and not game and not special and not user_communities and not my_communities %} +
    + {% nocontent "There are no communities of any kind yet. You can either create one in the admin panel, or make a user-created community once you sign up." %} +
    + {% endif %}