from ast import Delete, arg from itertools import chain from django import forms from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.core.cache import cache from django.core.cache.utils import make_template_fragment_key from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Count, Q, Value, BooleanField from django.db.utils import ProgrammingError from django.forms import Form, modelformset_factory from django.http import ( Http404, HttpResponsePermanentRedirect, HttpResponseRedirect, HttpResponseBadRequest, ) from django.shortcuts import get_object_or_404 from django.urls import reverse , reverse_lazy from django.utils import timezone from django.utils.html import format_html from django.utils.functional import cached_property from django.utils.safestring import mark_safe from django.utils.translation import gettext as _, gettext_lazy, ungettext from django.views.generic import ( DetailView, FormView, ListView, UpdateView, View, CreateView, DeleteView, ) from django.views.generic.detail import ( SingleObjectMixin, SingleObjectTemplateResponseMixin, ) from django.core.paginator import Paginator from reversion import revisions from judge.forms import ( EditOrganizationForm, AddOrganizationForm, AddOrganizationMemberForm, OrganizationBlogForm, OrganizationAdminBlogForm, EditOrganizationContestForm, ContestProblemFormSet, AddOrganizationContestForm, ) from judge.models import ( BlogPost, Comment, Organization, OrganizationRequest, Problem, Profile, Contest, Notification, ContestProblem, ) from judge import event_poster as event from judge.utils.ranker import ranker from judge.utils.views import ( TitleMixin, generic_message, QueryStringSortMixin, DiggPaginatorMixin, ) from judge.utils.problems import user_attempted_ids from judge.views.problem import ProblemList from judge.views.contests import ContestList from judge.views.submission import AllSubmissions, SubmissionsListBase __all__ = [ "OrganizationList", "OrganizationHome", "OrganizationUsers", "OrganizationProblems", "OrganizationContests", "OrganizationMembershipChange", "JoinOrganization", "LeaveOrganization", "EditOrganization", "RequestJoinOrganization", "OrganizationRequestDetail", "OrganizationRequestView", "OrganizationRequestLog", "KickUserWidgetView", ] class OrganizationBase(object): def can_edit_organization(self, org=None): if org is None: org = self.object if not self.request.user.is_authenticated: return False profile_id = self.request.profile.id return ( org.admins.filter(id=profile_id).exists() or org.registrant_id == profile_id or self.request.user.is_superuser ) def is_member(self, org=None): if org is None: org = self.object return ( self.request.profile in org if self.request.user.is_authenticated else False ) def can_access(self, org): if self.request.user.is_superuser: return True if org is None: org = self.object return self.is_member(org) or self.can_edit_organization(org) class OrganizationMixin(OrganizationBase): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["is_member"] = self.is_member(self.organization) context["can_edit"] = self.can_edit_organization(self.organization) context["organization"] = self.organization context["logo_override_image"] = self.organization.logo_override_image if "organizations" in context: context.pop("organizations") return context def dispatch(self, request, *args, **kwargs): try: self.organization_id = int(kwargs["pk"] ) self.organization = get_object_or_404(Organization, id=self.organization_id) except Http404: key = kwargs.get(self.slug_url_kwarg, None) if key: return generic_message( request, _("No such organization"), _('Could not find an organization with the key "%s".') % key, ) else: return generic_message( request, _("No such organization"), _("Could not find such organization."), ) if self.organization.slug != kwargs["slug"]: return HttpResponsePermanentRedirect( request.get_full_path().replace(kwargs["slug"], self.organization.slug) ) return super(OrganizationMixin, self).dispatch(request, *args, **kwargs) class AdminOrganizationMixin(OrganizationMixin): def dispatch(self, request, *args, **kwargs): res = super(AdminOrganizationMixin, self).dispatch(request, *args, **kwargs) if not hasattr(self, "organization") or self.can_edit_organization( self.organization ): return res return generic_message( request, _("Can't edit organization"), _("You are not allowed to edit this organization."), status=403, ) class MemberOrganizationMixin(OrganizationMixin): def dispatch(self, request, *args, **kwargs): res = super(MemberOrganizationMixin, self).dispatch(request, *args, **kwargs) if not hasattr(self, "organization") or self.can_access(self.organization): return res return generic_message( request, _("Can't access organization"), _("You are not allowed to access this organization."), status=403, ) class OrganizationHomeViewContext: def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if not hasattr(self, "organization"): self.organization = self.object if self.can_edit_organization(self.organization): context["pending_count"] = OrganizationRequest.objects.filter( state="P", organization=self.organization ).count() context["pending_blog_count"] = BlogPost.objects.filter( visible=False, organizations=self.organization ).count() else: context["pending_blog_count"] = BlogPost.objects.filter( visible=False, organizations=self.organization, authors=self.request.profile, ).count() context["top_rated"] = self.organization.members.filter( is_unlisted=False ).order_by("-rating")[:10] context["top_scorer"] = self.organization.members.filter( is_unlisted=False ).order_by("-performance_points")[:10] return context class OrganizationDetailView( OrganizationMixin, OrganizationHomeViewContext, DetailView ): context_object_name = "organization" model = Organization def get(self, request, *args, **kwargs): self.object = self.get_object() if self.object.slug != kwargs["slug"]: return HttpResponsePermanentRedirect( request.get_full_path().replace(kwargs["slug"], self.object.slug) ) context = self.get_context_data(object=self.object) return self.render_to_response(context) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["can_edit"] = self.can_edit_organization() context["is_member"] = self.is_member() return context class OrganizationList(TitleMixin, ListView, OrganizationBase): model = Organization context_object_name = "organizations" template_name = "organization/list.html" title = gettext_lazy("Groups") def get_queryset(self): return ( super(OrganizationList, self) .get_queryset() .annotate(member_count=Count("member")) ) def get_context_data(self, **kwargs): context = super(OrganizationList, self).get_context_data(**kwargs) context["my_organizations"] = [] context["page_type"] = "organizations" if self.request.profile: context["my_organizations"] = context["organizations"].filter( id__in=self.request.profile.organizations.values("id") ) other_organizations = context["organizations"].exclude( id__in=context["my_organizations"] ) context["open_organizations"] = other_organizations.filter(is_open=True) context["private_organizations"] = other_organizations.filter(is_open=False) return context class OrganizationHome(OrganizationDetailView): template_name = "organization/home.html" def get_posts(self): posts = ( BlogPost.objects.filter( visible=True, publish_on__lte=timezone.now(), is_organization_private=True, organizations=self.object, ) .order_by("-sticky", "-publish_on") .prefetch_related("authors__user", "organizations") ) paginator = Paginator(posts, 10) page_number = self.request.GET.get("page", 1) posts = paginator.get_page(page_number) return posts def get_context_data(self, **kwargs): context = super(OrganizationHome, self).get_context_data(**kwargs) context["title"] = self.object.name context["posts"] = self.get_posts() context["post_comment_counts"] = { int(page[2:]): count for page, count in Comment.objects.filter( page__in=["b:%d" % post.id for post in context["posts"]], hidden=False ) .values_list("page") .annotate(count=Count("page")) .order_by() } now = timezone.now() visible_contests = ( Contest.get_visible_contests(self.request.user) .filter( is_visible=True, is_organization_private=True, organizations=self.object ) .order_by("start_time") ) context["current_contests"] = visible_contests.filter( start_time__lte=now, end_time__gt=now ) context["future_contests"] = visible_contests.filter(start_time__gt=now) context["page_type"] = "home" return context class OrganizationUsers(QueryStringSortMixin, OrganizationDetailView): template_name = "organization/users.html" all_sorts = frozenset(("points", "problem_count", "rating", "performance_points")) default_desc = all_sorts default_sort = "-performance_points" def dispatch(self, request, *args, **kwargs): res = super(OrganizationUsers, self).dispatch(request, *args, **kwargs) if self.can_access(self.organization) or self.organization.is_open: return res return generic_message( request, _("Can't access organization"), _("You are not allowed to access this organization."), status=403, ) def get_context_data(self, **kwargs): context = super(OrganizationUsers, self).get_context_data(**kwargs) context["title"] = _("%s Members") % self.object.name context["partial"] = True context["kick_url"] = reverse( "organization_user_kick", args=[self.object.id, self.object.slug] ) context["users"] = ranker( self.get_object() .members.filter(is_unlisted=False) .order_by(self.order, "id") .select_related("user") .only( "display_rank", "user__username", "points", "rating", "performance_points", "problem_count", ) ) context["first_page_href"] = "." context["page_type"] = "users" context.update(self.get_sort_context()) return context class OrganizationProblems(LoginRequiredMixin, MemberOrganizationMixin, ProblemList): template_name = "organization/problems.html" def get_queryset(self): self.org_query = [self.organization_id] return super().get_normal_queryset() def get(self, request, *args, **kwargs): self.setup_problem_list(request) return super().get(request, *args, **kwargs) def get_latest_attempted_problems(self, limit=None): if self.in_contest or not self.profile: return () problems = set(self.get_queryset().values_list("code", flat=True)) result = list(user_attempted_ids(self.profile).values()) result = [i for i in result if i["code"] in problems] result = sorted(result, key=lambda d: -d["last_submission"]) if limit: result = result[:limit] return result def get_context_data(self, **kwargs): context = super(OrganizationProblems, self).get_context_data(**kwargs) context["page_type"] = "problems" return context class OrganizationContestMixin( LoginRequiredMixin, TitleMixin, OrganizationMixin, OrganizationHomeViewContext, ): model = Contest def is_contest_editable(self, request, contest): return request.profile in contest.authors.all() or self.can_edit_organization( self.organization ) class OrganizationContests( OrganizationContestMixin, MemberOrganizationMixin, ContestList ): template_name = "organization/contests.html" def get_queryset(self): self.org_query = [self.organization_id] return super().get_queryset() def set_editable_contest(self, contest): if not contest: return False contest.is_editable = self.is_contest_editable(self.request, contest) def get_context_data(self, **kwargs): context = super(OrganizationContests, self).get_context_data(**kwargs) context["page_type"] = "contests" context["hide_contest_orgs"] = True context.pop("organizations") context["create_url"] = reverse( "organization_contest_add", args=[self.organization.id, self.organization.slug], ) for participation in context["active_participations"]: self.set_editable_contest(participation.contest) for contest in context["past_contests"]: self.set_editable_contest(contest) for contest in context["current_contests"]: self.set_editable_contest(contest) for contest in context["future_contests"]: self.set_editable_contest(contest) return context class OrganizationSubmissions( LoginRequiredMixin, MemberOrganizationMixin, SubmissionsListBase ): template_name = "organization/submissions.html" @cached_property def in_contest(self): return False @cached_property def contest(self): return None def get_queryset(self): return ( super() ._get_queryset() .filter(contest_object__organizations=self.organization) ) def get_context_data(self, **kwargs): context = super(OrganizationSubmissions, self).get_context_data(**kwargs) # context["dynamic_update"] = context["page_obj"].number == 1 # context["last_msg"] = event.last() context["stats_update_interval"] = 3600 context["page_type"] = "submissions" context["page_prefix"] = None context["page_suffix"] = suffix = ( ("?" + self.request.GET.urlencode()) if self.request.GET else "" ) context["first_page_href"] = (self.first_page_href or ".") + suffix return context def get_content_title(self): return format_html( _('All submissions in {0}'), self.organization, reverse( "organization_home", args=[self.organization.id, self.organization.slug] ), ) class OrganizationMembershipChange( LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View ): model = Organization context_object_name = "organization" def post(self, request, *args, **kwargs): org = self.get_object() response = self.handle(request, org, request.profile) if response is not None: return response return HttpResponseRedirect(org.get_absolute_url()) def handle(self, request, org, profile): raise NotImplementedError() class JoinOrganization(OrganizationMembershipChange): def handle(self, request, org, profile): if profile.organizations.filter(id=org.id).exists(): return generic_message( request, _("Joining group"), _("You are already in the group."), ) if not org.is_open: return generic_message( request, _("Joining group"), _("This group is not open.") ) max_orgs = settings.DMOJ_USER_MAX_ORGANIZATION_COUNT if profile.organizations.filter(is_open=True).count() >= max_orgs: return generic_message( request, _("Joining group"), _("You may not be part of more than {count} public groups.").format( count=max_orgs ), ) profile.organizations.add(org) profile.save() cache.delete(make_template_fragment_key("org_member_count", (org.id,))) class LeaveOrganization(OrganizationMembershipChange): def handle(self, request, org, profile): if not profile.organizations.filter(id=org.id).exists(): return generic_message( request, _("Leaving group"), _('You are not in "%s".') % org.short_name, ) profile.organizations.remove(org) cache.delete(make_template_fragment_key("org_member_count", (org.id,))) class OrganizationRequestForm(Form): reason = forms.CharField(widget=forms.Textarea) class RequestJoinOrganization(LoginRequiredMixin, SingleObjectMixin, FormView): model = Organization slug_field = "key" slug_url_kwarg = "key" template_name = "organization/requests/request.html" form_class = OrganizationRequestForm def dispatch(self, request, *args, **kwargs): self.object = self.get_object() return super(RequestJoinOrganization, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super(RequestJoinOrganization, self).get_context_data(**kwargs) if self.object.is_open: raise Http404() context["title"] = _("Request to join %s") % self.object.name return context def form_valid(self, form): request = OrganizationRequest() request.organization = self.get_object() request.user = self.request.profile request.reason = form.cleaned_data["reason"] request.state = "P" request.save() return HttpResponseRedirect( reverse( "request_organization_detail", args=( request.organization.id, request.organization.slug, request.id, ), ) ) class OrganizationRequestDetail( LoginRequiredMixin, TitleMixin, OrganizationMixin, OrganizationHomeViewContext, DetailView, ): model = OrganizationRequest template_name = "organization/requests/detail.html" title = gettext_lazy("Join request detail") pk_url_kwarg = "rpk" def get_object(self, queryset=None): object = super(OrganizationRequestDetail, self).get_object(queryset) profile = self.request.profile if ( object.user_id != profile.id and not object.organization.admins.filter(id=profile.id).exists() ): raise PermissionDenied() return object OrganizationRequestFormSet = modelformset_factory( OrganizationRequest, extra=0, fields=("state",), can_delete=True ) class OrganizationRequestBaseView( OrganizationDetailView, TitleMixin, LoginRequiredMixin, SingleObjectTemplateResponseMixin, SingleObjectMixin, ): model = Organization slug_field = "key" slug_url_kwarg = "key" tab = None def get_object(self, queryset=None): organization = super(OrganizationRequestBaseView, self).get_object(queryset) if not ( organization.admins.filter(id=self.request.profile.id).exists() or organization.registrant_id == self.request.profile.id ): raise PermissionDenied() return organization def get_content_title(self): href = reverse("organization_home", args=[self.object.id, self.object.slug]) return mark_safe( f'Manage join requests for {self.object.name}' ) def get_context_data(self, **kwargs): context = super(OrganizationRequestBaseView, self).get_context_data(**kwargs) context["title"] = _("Managing join requests for %s") % self.object.name context["tab"] = self.tab return context class OrganizationRequestView(OrganizationRequestBaseView): template_name = "organization/requests/pending.html" tab = "pending" def get_context_data(self, **kwargs): context = super(OrganizationRequestView, self).get_context_data(**kwargs) context["formset"] = self.formset return context def get(self, request, *args, **kwargs): self.object = self.get_object() self.formset = OrganizationRequestFormSet( queryset=OrganizationRequest.objects.filter( state="P", organization=self.object ), ) context = self.get_context_data(object=self.object) return self.render_to_response(context) def post(self, request, *args, **kwargs): self.object = organization = self.get_object() self.formset = formset = OrganizationRequestFormSet(request.POST, request.FILES) if formset.is_valid(): if organization.slots is not None: deleted_set = set(formset.deleted_forms) to_approve = sum( form.cleaned_data["state"] == "A" for form in formset.forms if form not in deleted_set ) can_add = organization.slots - organization.members.count() if to_approve > can_add: messages.error( request, _( "Your organization can only receive %d more members. " "You cannot approve %d users." ) % (can_add, to_approve), ) return self.render_to_response( self.get_context_data(object=organization) ) approved, rejected = 0, 0 for obj in formset.save(): if obj.state == "A": obj.user.organizations.add(obj.organization) approved += 1 elif obj.state == "R": rejected += 1 messages.success( request, ungettext("Approved %d user.", "Approved %d users.", approved) % approved + "\n" + ungettext("Rejected %d user.", "Rejected %d users.", rejected) % rejected, ) cache.delete( make_template_fragment_key("org_member_count", (organization.id,)) ) return HttpResponseRedirect(request.get_full_path()) return self.render_to_response(self.get_context_data(object=organization)) put = post class OrganizationRequestLog(OrganizationRequestBaseView): states = ("A", "R") tab = "log" template_name = "organization/requests/log.html" def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) return self.render_to_response(context) def get_context_data(self, **kwargs): context = super(OrganizationRequestLog, self).get_context_data(**kwargs) context["requests"] = self.object.requests.filter(state__in=self.states) return context class AddOrganizationMember( LoginRequiredMixin, TitleMixin, AdminOrganizationMixin, OrganizationDetailView, UpdateView, ): template_name = "organization/add-member.html" model = Organization form_class = AddOrganizationMemberForm def get_title(self): return _("Add member for %s") % self.object.name def get_object(self, queryset=None): object = super(AddOrganizationMember, self).get_object() if not self.can_edit_organization(object): raise PermissionDenied() return object def form_valid(self, form): new_users = form.cleaned_data["new_users"] self.object.members.add(*new_users) with transaction.atomic(), revisions.create_revision(): revisions.set_comment(_("Added members from site")) revisions.set_user(self.request.user) return super(AddOrganizationMember, self).form_valid(form) def get_success_url(self): return reverse("organization_users", args=[self.object.id, self.object.slug]) class KickUserWidgetView( LoginRequiredMixin, AdminOrganizationMixin, SingleObjectMixin, View ): model = Organization def post(self, request, *args, **kwargs): organization = self.get_object() try: user = Profile.objects.get(id=request.POST.get("user", None)) except Profile.DoesNotExist: return generic_message( request, _("Can't kick user"), _("The user you are trying to kick does not exist!"), status=400, ) if not organization.members.filter(id=user.id).exists(): return generic_message( request, _("Can't kick user"), _("The user you are trying to kick is not in organization: %s.") % organization.name, status=400, ) organization.members.remove(user) return HttpResponseRedirect(organization.get_users_url()) class EditOrganization( LoginRequiredMixin, TitleMixin, AdminOrganizationMixin, OrganizationDetailView, UpdateView, ): template_name = "organization/edit.html" model = Organization form_class = EditOrganizationForm def get_title(self): return _("Edit %s") % self.object.name def get_object(self, queryset=None): object = super(EditOrganization, self).get_object() if not self.can_edit_organization(object): raise PermissionDenied() return object def get_form(self, form_class=None): form = super(EditOrganization, self).get_form(form_class) form.fields["admins"].queryset = Profile.objects.filter( Q(organizations=self.object) | Q(admin_of=self.object) ).distinct() return form def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): revisions.set_comment(_("Edited from site")) revisions.set_user(self.request.user) return super(EditOrganization, self).form_valid(form) class AddOrganization(LoginRequiredMixin, TitleMixin, CreateView): template_name = "organization/add.html" model = Organization form_class = AddOrganizationForm def get_title(self): return _("Create group") def get_form_kwargs(self): kwargs = super(AddOrganization, self).get_form_kwargs() kwargs["request"] = self.request return kwargs def form_valid(self, form): if ( not self.request.user.is_staff and Organization.objects.filter(registrant=self.request.profile).count() >= settings.DMOJ_USER_MAX_ORGANIZATION_ADD ): return generic_message( self.request, _("Exceeded limit"), _("You created too many groups. You can only create at most %d groups") % settings.DMOJ_USER_MAX_ORGANIZATION_ADD, status=400, ) with transaction.atomic(), revisions.create_revision(): revisions.set_comment(_("Added from site")) revisions.set_user(self.request.user) res = super(AddOrganization, self).form_valid(form) self.object.admins.add(self.request.profile) self.object.members.add(self.request.profile) self.object.save() return res class AddOrganizationContest( AdminOrganizationMixin, OrganizationContestMixin, CreateView ): template_name = "organization/contest/add.html" form_class = AddOrganizationContestForm def get_title(self): return _("Add contest") def get_form_kwargs(self): kwargs = super(AddOrganizationContest, self).get_form_kwargs() kwargs["request"] = self.request return kwargs def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): revisions.set_comment(_("Added from site")) revisions.set_user(self.request.user) res = super(AddOrganizationContest, self).form_valid(form) self.object.organizations.add(self.organization) self.object.is_organization_private = True self.object.authors.add(self.request.profile) self.object.save() return res def get_success_url(self): return reverse( "organization_contest_edit", args=[self.organization.id, self.organization.slug, self.object.key], ) class EditOrganizationContest( OrganizationContestMixin, MemberOrganizationMixin, UpdateView ): template_name = "organization/contest/edit.html" form_class = EditOrganizationContestForm def setup_contest(self, request, *args, **kwargs): contest_key = kwargs.get("contest", None) if not contest_key: raise Http404() self.contest = get_object_or_404(Contest, key=contest_key) if self.organization not in self.contest.organizations.all(): raise Http404() if not self.is_contest_editable(request, self.contest): return generic_message( self.request, _("Permission denied"), _("You are not allowed to edit this contest"), status=400, ) def get_form_kwargs(self): kwargs = super(EditOrganizationContest, self).get_form_kwargs() kwargs["org_id"] = self.organization.id return kwargs def get(self, request, *args, **kwargs): res = self.setup_contest(request, *args, **kwargs) if res: return res return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): res = self.setup_contest(request, *args, **kwargs) if res: return res problem_formset = self.get_problem_formset(True) if problem_formset.is_valid(): for problem_form in problem_formset.save(commit=False): if problem_form: problem_form.contest = self.contest problem_form.save() for problem_form in problem_formset.deleted_objects: problem_form.delete() return super().post(request, *args, **kwargs) self.object = self.contest return self.render_to_response( self.get_context_data( problems_form=problem_formset, ) ) def get_title(self): return _("Edit %s") % self.contest.key def get_content_title(self): href = reverse("contest_view", args=[self.contest.key]) return mark_safe(f'Edit {self.contest.key}') def get_object(self): return self.contest def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): revisions.set_comment(_("Edited from site")) revisions.set_user(self.request.user) res = super(EditOrganizationContest, self).form_valid(form) self.object.organizations.add(self.organization) self.object.is_organization_private = True self.object.save() return res def get_problem_formset(self, post=False): return ContestProblemFormSet( data=self.request.POST if post else None, prefix="problems", queryset=ContestProblem.objects.filter(contest=self.contest).order_by( "order" ), ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if "problems_form" not in context: context["problems_form"] = self.get_problem_formset() return context def get_success_url(self): return self.request.path class AddOrganizationBlog( LoginRequiredMixin, TitleMixin, OrganizationHomeViewContext, MemberOrganizationMixin, CreateView, ): template_name = "organization/blog/add.html" model = BlogPost form_class = OrganizationBlogForm def get_form_class(self): if self.can_edit_organization(self.organization): return OrganizationAdminBlogForm return OrganizationBlogForm def get_title(self): return _("Add blog for %s") % self.organization.name def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): res = super(AddOrganizationBlog, self).form_valid(form) self.object.is_organization_private = True self.object.authors.add(self.request.profile) self.object.slug = self.organization.slug + "-" + self.request.user.username self.object.organizations.add(self.organization) self.object.save() revisions.set_comment(_("Added from site")) revisions.set_user(self.request.user) link = reverse( "edit_organization_blog", args=[self.organization.id, self.organization.slug, self.object.id], ) html = ( f'{self.object.title} - {self.organization.name}' ) for user in self.organization.admins.all(): if user.id == self.request.profile.id: continue notification = Notification( owner=user, author=self.request.profile, category="Add blog", html_link=html, ) notification.save() return res class EditOrganizationBlog( LoginRequiredMixin, TitleMixin, OrganizationHomeViewContext, MemberOrganizationMixin, UpdateView, ): template_name = "organization/blog/edit.html" model = BlogPost def get_form_class(self): if self.can_edit_organization(self.organization): return OrganizationAdminBlogForm return OrganizationBlogForm def setup_blog(self, request, *args, **kwargs): try: self.blog_id = kwargs["blog_pk"] self.blog = BlogPost.objects.get(id=self.blog_id) if self.organization not in self.blog.organizations.all(): raise Exception("This blog does not belong to this organization") if ( self.request.profile not in self.blog.authors.all() and not self.can_edit_organization(self.organization) ): raise Exception("Not allowed to edit this blog") except: return generic_message( request, _("Permission denied"), _("Not allowed to edit this blog"), ) def delete_blog(self , request , *args , **kwargs): self.blog_id = kwargs["blog_pk"] BlogPost.objects.get(pk = self.blog_id).delete() def get(self, request, *args, **kwargs): res = self.setup_blog(request, *args, **kwargs) if res: return res return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): res = self.setup_blog(request, *args, **kwargs) if res: return res if request.POST['action'] == 'Delete': self.create_notification("Delete blog") self.delete_blog(request , *args , **kwargs) cur_url = reverse("organization_pending_blogs", args=(self.organization_id,self.organization.slug) ) return HttpResponseRedirect(cur_url) else: return super().post(request, *args, **kwargs) def get_object(self): return self.blog def get_title(self): return _("Edit blog %s") % self.object.title def create_notification(self,action): blog = BlogPost.objects.get(pk=self.blog_id) link = reverse( "edit_organization_blog", args=[self.organization.id, self.organization.slug, self.blog_id], ) html = ( f'{blog.title} - {self.organization.name}' ) post_authors = blog.authors.all() posible_user = self.organization.admins.all() | post_authors for user in posible_user: if user.id == self.request.profile.id: continue notification = Notification( owner=user, author=self.request.profile, category= action, html_link=html, ) notification.save() def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): res = super(EditOrganizationBlog, self).form_valid(form) revisions.set_comment(_("Edited from site")) revisions.set_user(self.request.user) self.create_notification("Edit blog") return res class PendingBlogs( LoginRequiredMixin, TitleMixin, MemberOrganizationMixin, OrganizationHomeViewContext, ListView, ): model = BlogPost template_name = "organization/blog/pending.html" context_object_name = "blogs" def get_queryset(self): queryset = BlogPost.objects.filter( organizations=self.organization, visible=False ) if not self.can_edit_organization(self.organization): queryset = queryset.filter(authors=self.request.profile) return queryset.order_by("publish_on") def get_title(self): return _("Pending blogs in %s") % self.organization.name