diff --git a/backend/settings.py b/backend/settings.py index 112e267c..b9f47a4b 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -13,6 +13,8 @@ DEBUG = True ALLOWED_HOSTS = ['*'] +CORS_ORIGIN_WHITELIST = ['https://www.insti.app','https://api.insti.app', 'https://gymkhana.iitb.ac.in', 'http://10.105.177.175', 'http://localhost:4200', 'http://10.198.49.175'] +CORS_ALLOW_CREDENTIALS = True # SSO Config SSO_TOKEN_URL = 'https://gymkhana.iitb.ac.in/sso/oauth/token/' diff --git a/community/models.py b/community/models.py index 51ac991c..67d31e5b 100644 --- a/community/models.py +++ b/community/models.py @@ -2,6 +2,7 @@ from uuid import uuid4 from django.db import models from helpers.misc import get_url_friendly +from users.models import UserProfile class Community(models.Model): id = models.UUIDField(primary_key=True, default=uuid4, editable=False) diff --git a/community/serializer_min.py b/community/serializer_min.py index 024ae70e..1576b123 100644 --- a/community/serializer_min.py +++ b/community/serializer_min.py @@ -3,6 +3,7 @@ from achievements.serializers import InterestSerializer from bodies.serializer_min import BodySerializerMin from community.models import Community, CommunityPost +from users.models import UserProfile from users.serializers import UserProfileSerializer class CommunitySerializerMin(serializers.ModelSerializer): @@ -36,7 +37,8 @@ class CommunityPostSerializerMin(serializers.ModelSerializer): reactions_count = serializers.SerializerMethodField() user_reaction = serializers.SerializerMethodField() comments_count = serializers.SerializerMethodField() - posted_by = UserProfileSerializer(read_only=True) + # posted_by = UserProfileSerializer(read_only=True) + posted_by = serializers.SerializerMethodField() image_url = serializers.SerializerMethodField() most_liked_comment = serializers.SerializerMethodField() community = CommunitySerializerMin(read_only=True) @@ -45,6 +47,17 @@ class CommunityPostSerializerMin(serializers.ModelSerializer): interests = InterestSerializer(read_only=True, many=True) has_user_reported = serializers.SerializerMethodField() + def get_posted_by(self, obj): + pb = UserProfile.objects.get(id=obj.posted_by.id) + if(obj.anonymous and "return_for_mod" in self.context and not self.context["return_for_mod"]): + pb.name = "Anonymous" + pb.id = "null" + pb.ldap_id = "null" + pb.profile_pic = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSM9q9XJKxlskry5gXTz1OXUyem5Ap59lcEGg&usqp=CAU" + elif(obj.anonymous and "return_for_mod" in self.context and self.context["return_for_mod"]): + pb.name += " (Anon)" + return UserProfileSerializer(pb).data + def get_most_liked_comment(self, obj): """Get the most liked comment of the community post """ queryset = obj.comments.filter(deleted=False, status=1) diff --git a/community/serializers.py b/community/serializers.py index 87b30bba..cc4529c8 100644 --- a/community/serializers.py +++ b/community/serializers.py @@ -5,6 +5,7 @@ from roles.serializers import RoleSerializerMin from users.models import UserProfile from bodies.models import Body +from users.serializers import UserProfileSerializer class CommunitySerializers(serializers.ModelSerializer): @@ -57,10 +58,22 @@ def setup_eager_loading(queryset, request): class CommunityPostSerializers(CommunityPostSerializerMin): comments = serializers.SerializerMethodField() + posted_by = serializers.SerializerMethodField() def get_comments(self, obj): comments = obj.comments.filter(deleted=False, status=1) return CommunityPostSerializerMin(comments, many=True).data + + def get_posted_by(self, obj): + pb = UserProfile.objects.get(id=obj.posted_by.id) + if(obj.anonymous and "return_for_mod" in self.context and not self.context["return_for_mod"]): + pb.name = "Anonymous" + pb.id = "null" + pb.ldap_id = "null" + pb.profile_pic = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSM9q9XJKxlskry5gXTz1OXUyem5Ap59lcEGg&usqp=CAU" + elif(obj.anonymous and "return_for_mod" in self.context and self.context["return_for_mod"]): + pb.name += " (Anon)" + return UserProfileSerializer(pb).data class Meta: model = CommunityPost diff --git a/community/urls.py b/community/urls.py index c3407a9a..2e660f02 100644 --- a/community/urls.py +++ b/community/urls.py @@ -5,6 +5,7 @@ urlpatterns = [ path('communities', CommunityViewSet.as_view({ 'get': 'list', + 'post': 'create' })), # viewing the list of communities path('communities/', CommunityViewSet.as_view({ diff --git a/community/views.py b/community/views.py index c8021d9f..19eb3a82 100644 --- a/community/views.py +++ b/community/views.py @@ -11,7 +11,7 @@ from roles.helpers import forbidden_no_privileges from helpers.misc import query_from_num from helpers.misc import query_search - +from users.models import UserProfile class ModeratorViewSet(viewsets.ModelViewSet): queryset = CommunityPost.objects serializer_class = CommunityPostSerializers @@ -62,7 +62,10 @@ def retrieve_full(self, request, pk): self.queryset = CommunityPostSerializers.setup_eager_loading(self.queryset, request) post = self.get_community_post(pk) - serialized = CommunityPostSerializers(post, context={'request': request}).data + return_for_mod = False + if(user_has_privilege(request.user.profile, post.community.body.id, "AppP")): + return_for_mod = True + serialized = CommunityPostSerializers(post, context={'return_for_mod': return_for_mod}).data return Response(serialized) @@ -73,24 +76,32 @@ def list(self, request): # Check for time and date filtered query params status = request.GET.get("status") + comm_id = request.GET.get("community") + if comm_id is None: + return Response({"message": "comm_id is required"}, status=400) + community = get_object_or_404(Community.objects, id=comm_id) # If your posts if status is None: - queryset = CommunityPost.objects.filter(thread_rank=1, posted_by=request.user + queryset = CommunityPost.objects.filter(thread_rank=1, community=community, posted_by=request.user .profile).order_by("-time_of_modification") else: # If reported posts if status == "3": - queryset = CommunityPost.objects.filter(status=status, deleted=False).order_by("-time_of_modification") + queryset = CommunityPost.objects.filter( + status=status, community=community, deleted=False).order_by("-time_of_modification") # queryset = CommunityPost.objects.all() else: queryset = CommunityPost.objects.filter( - status=status, deleted=False, thread_rank=1).order_by("-time_of_modification") + status=status, community=community, deleted=False, thread_rank=1).order_by("-time_of_modification") queryset = query_search(request, 3, queryset, ['content'], 'posts') queryset = query_from_num(request, 20, queryset) + return_for_mod = False + if(user_has_privilege(request.user.profile, community.body.id, "AppP")): + return_for_mod = True - serializer = CommunityPostSerializerMin(queryset, many=True, context={'request': request}) + serializer = CommunityPostSerializerMin(queryset, many=True, context={'return_for_mod': return_for_mod}) data = serializer.data return Response({'count': len(data), 'data': data}) @@ -103,6 +114,7 @@ def create(self, request): if 'community' not in request.data or not request.data['community']: return forbidden_no_privileges() + user, created = UserProfile.objects.get_or_create(user=request.user) return super().create(request) @login_required_ajax @@ -203,3 +215,13 @@ def get_community(self, pk): return get_object_or_404(self.queryset, id=pk) except ValueError: return get_object_or_404(self.queryset, str_id=pk) + + @login_required_ajax + def create(self, request): + name = request.data['name'] + user, created = UserProfile.objects.get_or_create(user=request.user) + if not Community.objects.all().filter(name=name).exists(): + super().create(request) + return Response({"message": "Community created"}) + + return Response({"message": "Community already exists"}, status=400) diff --git a/other/tasks.py b/other/tasks.py index b5f58234..f59aaa34 100644 --- a/other/tasks.py +++ b/other/tasks.py @@ -78,8 +78,9 @@ def notify_new_commpost(pk): for interest in instance.interests.all(): users = User.objects.filter( - id__in=UserInterest.filter(title=interest.title).user.filter(active=True).values('user_id') - ) + + id__in=UserInterest.objects.filter(title=interest.title)).filter(is_active=True).values('id') + notify.send( instance, recipient=users, diff --git a/roles/admin.py b/roles/admin.py index 6feb8101..1226a158 100644 --- a/roles/admin.py +++ b/roles/admin.py @@ -7,6 +7,7 @@ class BodyRoleAdmin(admin.ModelAdmin): list_display = ('name', 'body', 'permissions') search_fields = ('body__name', 'name') + class InstittuteRoleAdmin(admin.ModelAdmin): list_display = ('name', 'permissions') search_fields = ['name'] diff --git a/roles/helpers.py b/roles/helpers.py index 725faa27..7e2ffbf4 100644 --- a/roles/helpers.py +++ b/roles/helpers.py @@ -27,6 +27,8 @@ def user_has_community_privilege(profile, communityid, privilege): return True return False + + def user_has_insti_privilege(profile, privilege): """Returns true if UserProfile has the institute privilege.""" return check_roles(profile.institute_roles.all(), privilege) diff --git a/roles/migrations/0020_communityrole.py b/roles/migrations/0020_communityrole.py new file mode 100644 index 00000000..be8a289a --- /dev/null +++ b/roles/migrations/0020_communityrole.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.16 on 2023-07-07 14:40 + +from django.db import migrations, models +import django.db.models.deletion +import multiselectfield.db.fields +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('community', '0028_auto_20221003_2130'), + ('roles', '0019_alter_bodyrole_permissions'), + ] + + operations = [ + migrations.CreateModel( + name='CommunityRole', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('time_of_creation', models.DateTimeField(auto_now_add=True)), + ('name', models.CharField(max_length=50)), + ('inheritable', models.BooleanField(default=False)), + ('permissions', multiselectfield.db.fields.MultiSelectField(choices=[('AddE', 'Add Event'), ('UpdE', 'Update Event'), ('DelE', 'Delete Event'), ('UpdB', 'Update Body'), ('Role', 'Modify Roles'), ('VerA', 'Verify Achievements'), ('AppP', 'Moderate Post'), ('ModC', 'Moderate Comment')], max_length=39)), + ('priority', models.IntegerField(default=0)), + ('official_post', models.BooleanField(default=True)), + ('permanent', models.BooleanField(default=False)), + ('community', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='roles', to='community.community')), + ], + options={ + 'verbose_name': 'Community Role', + 'verbose_name_plural': 'Community Roles', + 'ordering': ('community__name', 'priority'), + }, + ), + ] diff --git a/roles/migrations/0021_delete_communityrole.py b/roles/migrations/0021_delete_communityrole.py new file mode 100644 index 00000000..8905be25 --- /dev/null +++ b/roles/migrations/0021_delete_communityrole.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.16 on 2023-07-22 13:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0043_remove_userprofile_community_roles'), + ('roles', '0020_communityrole'), + ] + + operations = [ + migrations.DeleteModel( + name='CommunityRole', + ), + ] diff --git a/roles/models.py b/roles/models.py index a1267b24..fb0bff80 100644 --- a/roles/models.py +++ b/roles/models.py @@ -34,6 +34,31 @@ class Meta: def __str__(self): return self.body.name + " " + self.name + +''' +Added Community Role Model : To allow to have various communities in a single body and have different roles for each community. +Ditched For Now +''' +# class CommunityRole(models.Model): +# """A role for a bodywhich can be granted to multiple users.""" + +# id = models.UUIDField(primary_key=True, default=uuid4, editable=False) +# time_of_creation = models.DateTimeField(auto_now_add=True) +# name = models.CharField(max_length=50) +# community = models.ForeignKey('community.Community', on_delete=models.CASCADE, related_name='roles') +# inheritable = models.BooleanField(default=False) +# permissions = MultiSelectField(choices=PERMISSION_CHOICES) +# priority = models.IntegerField(default=0) +# official_post = models.BooleanField(default=True) +# permanent = models.BooleanField(default=False) + +# class Meta: +# verbose_name = "Community Role" +# verbose_name_plural = "Community Roles" +# ordering = ("community__name", "priority") + +# def __str__(self): +# return self.community.name + " " + self.name INSTITUTE_PERMISSION_CHOICES = ( diff --git a/users/migrations/0041_userprofile_community_roles.py b/users/migrations/0041_userprofile_community_roles.py new file mode 100644 index 00000000..b3e813c2 --- /dev/null +++ b/users/migrations/0041_userprofile_community_roles.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.16 on 2023-07-07 14:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('roles', '0020_communityrole'), + ('users', '0040_remove_userprofile_followed_communities'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='community_roles', + field=models.ManyToManyField(blank=True, related_name='users', to='roles.CommunityRole'), + ), + ] diff --git a/users/migrations/0042_userprofile_followed_communities.py b/users/migrations/0042_userprofile_followed_communities.py new file mode 100644 index 00000000..279ccd87 --- /dev/null +++ b/users/migrations/0042_userprofile_followed_communities.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.16 on 2023-07-07 23:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('community', '0028_auto_20221003_2130'), + ('users', '0041_userprofile_community_roles'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='followed_communities', + field=models.ManyToManyField(blank=True, related_name='community_followers', to='community.Community'), + ), + ] diff --git a/users/migrations/0043_remove_userprofile_community_roles.py b/users/migrations/0043_remove_userprofile_community_roles.py new file mode 100644 index 00000000..a588305a --- /dev/null +++ b/users/migrations/0043_remove_userprofile_community_roles.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.16 on 2023-07-22 13:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0042_userprofile_followed_communities'), + ] + + operations = [ + migrations.RemoveField( + model_name='userprofile', + name='community_roles', + ), + ] diff --git a/users/migrations/0044_remove_userprofile_followed_communities.py b/users/migrations/0044_remove_userprofile_followed_communities.py new file mode 100644 index 00000000..899f2f26 --- /dev/null +++ b/users/migrations/0044_remove_userprofile_followed_communities.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.16 on 2023-07-24 18:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0043_remove_userprofile_community_roles'), + ] + + operations = [ + migrations.RemoveField( + model_name='userprofile', + name='followed_communities', + ), + ] diff --git a/users/models.py b/users/models.py index 4e4b3ad5..61a38c42 100644 --- a/users/models.py +++ b/users/models.py @@ -48,13 +48,14 @@ class UserProfile(models.Model): # InstiApp feature fields active = models.BooleanField(default=True) followed_bodies = models.ManyToManyField('bodies.Body', related_name='followers', blank=True) + # InstiApp roles roles = models.ManyToManyField('roles.BodyRole', related_name='users', blank=True) former_roles = models.ManyToManyField( 'roles.BodyRole', related_name='former_users', blank=True, through='UserFormerRole') institute_roles = models.ManyToManyField( 'roles.InstituteRole', related_name='users', blank=True) - + # community_roles = models.ManyToManyField('roles.CommunityRole', related_name='users', blank=True) # User exposed fields show_contact_no = models.BooleanField(default=False) fcm_id = models.CharField(max_length=200, null=True, blank=True)