Skip to content

Commit

Permalink
Merge pull request #5523 from nyaruka/cleaner_views_pt3
Browse files Browse the repository at this point in the history
Start adding base classes for org level views
  • Loading branch information
rowanseymour authored Oct 3, 2024
2 parents 7b31969 + d1b8144 commit b3e9a49
Show file tree
Hide file tree
Showing 29 changed files with 179 additions and 166 deletions.
2 changes: 1 addition & 1 deletion temba/airtime/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from temba.airtime.models import AirtimeTransfer
from temba.contacts.models import URN, ContactURN
from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.request_logs.models import HTTPLog
from temba.utils.views import SpaMixin

Expand Down
2 changes: 1 addition & 1 deletion temba/api/v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
from temba.globals.models import Global
from temba.locations.models import AdminBoundary, BoundaryAlias
from temba.msgs.models import Broadcast, BroadcastMsgCount, Label, LabelCount, Media, Msg, OptIn, SystemLabel
from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.models import OrgMembership, User
from temba.orgs.views.mixins import OrgPermsMixin
from temba.tickets.models import Ticket, TicketCount, Topic
from temba.utils import str_to_bool
from temba.utils.uuid import is_uuid
Expand Down
2 changes: 1 addition & 1 deletion temba/archives/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from django.http import HttpResponseRedirect

from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.utils.views import SpaMixin

from .models import Archive
Expand Down
2 changes: 1 addition & 1 deletion temba/campaigns/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ def test_archive_and_activate(self):

# can't archive campaign from other org
response = self.client.post(reverse("campaigns.campaign_archive", args=[other_org_campaign.id]))
self.assertEqual(404, response.status_code)
self.assertEqual(302, response.status_code)

# check object is unchanged
other_org_campaign.refresh_from_db()
Expand Down
13 changes: 7 additions & 6 deletions temba/campaigns/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from smartmin.views import SmartCreateView, SmartCRUDL, SmartDeleteView, SmartListView, SmartReadView, SmartUpdateView
from smartmin.views import SmartCreateView, SmartCRUDL, SmartDeleteView, SmartReadView, SmartUpdateView

from django import forms
from django.contrib import messages
Expand All @@ -11,8 +11,9 @@
from temba.contacts.models import ContactField, ContactGroup
from temba.flows.models import Flow
from temba.msgs.models import Msg
from temba.orgs.mixins import OrgFilterMixin, OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views import BaseMenuView, ModalMixin
from temba.orgs.views import ModalMixin
from temba.orgs.views.base import BaseListView, BaseMenuView
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.utils import languages
from temba.utils.fields import CompletionTextarea, InputWidget, SelectWidget, TembaChoiceField
from temba.utils.views import BulkActionMixin, ContentMenuMixin, SpaMixin
Expand Down Expand Up @@ -161,7 +162,7 @@ def get_form_kwargs(self):
kwargs["org"] = self.request.org
return kwargs

class BaseList(SpaMixin, ContentMenuMixin, OrgFilterMixin, OrgPermsMixin, BulkActionMixin, SmartListView):
class BaseList(SpaMixin, ContentMenuMixin, BulkActionMixin, BaseListView):
fields = ("name", "group")
default_template = "campaigns/campaign_list.html"
default_order = ("-modified_on",)
Expand Down Expand Up @@ -205,7 +206,7 @@ def get_queryset(self, *args, **kwargs):
qs = qs.filter(is_active=True, is_archived=True)
return qs

class Archive(OrgFilterMixin, OrgPermsMixin, SmartUpdateView):
class Archive(OrgObjPermsMixin, SmartUpdateView):
fields = ()
success_url = "[email protected]_read"
success_message = _("Campaign archived")
Expand All @@ -214,7 +215,7 @@ def save(self, obj):
obj.apply_action_archive(self.request.user, Campaign.objects.filter(id=obj.id))
return obj

class Activate(OrgFilterMixin, OrgPermsMixin, SmartUpdateView):
class Activate(OrgObjPermsMixin, SmartUpdateView):
fields = ()
success_url = "[email protected]_read"
success_message = _("Campaign activated")
Expand Down
2 changes: 1 addition & 1 deletion temba/channels/types/plivo/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from temba.channels.models import Channel
from temba.channels.views import BaseClaimNumberMixin, ChannelTypeMixin, ClaimViewMixin
from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.views.mixins import OrgPermsMixin
from temba.utils import countries
from temba.utils.fields import SelectWidget
from temba.utils.http import http_headers
Expand Down
2 changes: 1 addition & 1 deletion temba/channels/types/twilio/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.views.mixins import OrgPermsMixin
from temba.utils import countries
from temba.utils.fields import InputWidget, SelectWidget
from temba.utils.timezones import timezone_to_country_code
Expand Down
2 changes: 1 addition & 1 deletion temba/channels/types/vonage/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.views.mixins import OrgPermsMixin
from temba.utils import countries
from temba.utils.fields import InputWidget, SelectWidget
from temba.utils.models import generate_uuid
Expand Down
2 changes: 1 addition & 1 deletion temba/channels/types/whatsapp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from django.utils.translation import gettext_lazy as _

from temba.channels.views import ChannelTypeMixin
from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views import ModalMixin
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.utils.fields import InputWidget
from temba.utils.text import truncate
from temba.utils.views import ContentMenuMixin
Expand Down
2 changes: 1 addition & 1 deletion temba/channels/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
from temba.ivr.models import Call
from temba.msgs.models import Msg
from temba.notifications.views import NotificationTargetMixin
from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views import DependencyDeleteModal, ModalMixin
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.utils import countries
from temba.utils.fields import SelectWidget
from temba.utils.json import EpochEncoder
Expand Down
2 changes: 1 addition & 1 deletion temba/classifiers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views import DependencyDeleteModal
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.utils.views import ComponentFormMixin, ContentMenuMixin, SpaMixin

from .models import Classifier
Expand Down
5 changes: 3 additions & 2 deletions temba/contacts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
from temba.channels.models import Channel
from temba.mailroom.events import Event
from temba.notifications.views import NotificationTargetMixin
from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.models import User
from temba.orgs.views import BaseExportView, BaseMenuView, DependencyDeleteModal, DependencyUsagesModal, ModalMixin
from temba.orgs.views import BaseExportView, DependencyDeleteModal, DependencyUsagesModal, ModalMixin
from temba.orgs.views.base import BaseMenuView
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.tickets.models import Ticket, Topic
from temba.utils import json, on_transaction_commit
from temba.utils.dates import datetime_to_timestamp, timestamp_to_datetime
Expand Down
2 changes: 1 addition & 1 deletion temba/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from django.utils.translation import gettext_lazy as _

from temba.channels.models import Channel, ChannelCount
from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.models import Org
from temba.orgs.views.mixins import OrgPermsMixin
from temba.utils.views import SpaMixin

flattened_colors = [
Expand Down
11 changes: 6 additions & 5 deletions temba/flows/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@
from temba.flows.models import Flow, FlowRevision, FlowRun, FlowSession, FlowStart
from temba.flows.tasks import update_session_wait_expires
from temba.ivr.models import Call
from temba.orgs.mixins import OrgFilterMixin, OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.models import IntegrationType, Org
from temba.orgs.views import BaseExportView, BaseMenuView, DependencyDeleteModal, ModalMixin
from temba.orgs.views import BaseExportView, DependencyDeleteModal, ModalMixin
from temba.orgs.views.base import BaseListView, BaseMenuView
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.triggers.models import Trigger
from temba.utils import analytics, gettext, json, languages, on_transaction_commit
from temba.utils.fields import (
Expand Down Expand Up @@ -665,7 +666,7 @@ def update_triggers(self, flow, user, new_keywords: list):
match_type=Trigger.MATCH_FIRST_WORD,
)

class BaseList(SpaMixin, OrgFilterMixin, OrgPermsMixin, BulkActionMixin, ContentMenuMixin, SmartListView):
class BaseList(SpaMixin, BulkActionMixin, ContentMenuMixin, BaseListView):
permission = "flows.flow_list"
title = _("Flows")
refresh = 10000
Expand Down Expand Up @@ -1846,7 +1847,7 @@ class FlowStartCRUDL(SmartCRUDL):
model = FlowStart
actions = ("list", "interrupt", "status")

class List(SpaMixin, OrgFilterMixin, OrgPermsMixin, SmartListView):
class List(SpaMixin, BaseListView):
title = _("Flow Starts")
ordering = ("-created_on",)
select_related = ("flow", "created_by")
Expand Down Expand Up @@ -1877,7 +1878,7 @@ def get_context_data(self, *args, **kwargs):

return context

class Status(OrgPermsMixin, OrgFilterMixin, SmartListView):
class Status(BaseListView):
permission = "flows.flowstart_list"

def derive_queryset(self, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion temba/globals/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from django import forms
from django.urls import reverse

from temba.orgs.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.views import DependencyDeleteModal, DependencyUsagesModal, ModalMixin
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.utils.fields import InputWidget
from temba.utils.views import ContentMenuMixin, SpaMixin

Expand Down
2 changes: 1 addition & 1 deletion temba/locations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.views.decorators.csrf import csrf_exempt

from temba.locations.models import AdminBoundary, BoundaryAlias
from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.views.mixins import OrgPermsMixin
from temba.utils import json
from temba.utils.views import ContentMenuMixin, SpaMixin

Expand Down
4 changes: 2 additions & 2 deletions temba/msgs/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2612,8 +2612,8 @@ def test_status(self):
)

status_url = f"{reverse('msgs.broadcast_status')}?id={broadcast.id}&status=P"
self.assertRequestDisallowed(status_url, [None, self.user, self.agent])
response = self.assertReadFetch(status_url, [self.editor, self.admin])
self.assertRequestDisallowed(status_url, [None, self.agent])
response = self.assertReadFetch(status_url, [self.user, self.editor, self.admin])

# status returns json
self.assertEqual("Pending", response.json()["results"][0]["status"])
Expand Down
9 changes: 5 additions & 4 deletions temba/msgs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
from temba import mailroom
from temba.archives.models import Archive
from temba.mailroom.client.types import Exclusions
from temba.orgs.mixins import OrgFilterMixin, OrgObjPermsMixin, OrgPermsMixin
from temba.orgs.models import Org
from temba.orgs.views import BaseExportView, BaseMenuView, DependencyDeleteModal, DependencyUsagesModal, ModalMixin
from temba.orgs.views import BaseExportView, DependencyDeleteModal, DependencyUsagesModal, ModalMixin
from temba.orgs.views.base import BaseListView, BaseMenuView
from temba.orgs.views.mixins import OrgObjPermsMixin, OrgPermsMixin
from temba.schedules.views import ScheduleFormMixin
from temba.templates.models import Template, TemplateTranslation
from temba.utils import json, languages
Expand Down Expand Up @@ -704,8 +705,8 @@ def form_valid(self, form):

return self.render_modal_response(form)

class Status(OrgPermsMixin, OrgFilterMixin, SmartListView):
permission = "msgs.broadcast_read"
class Status(BaseListView):
permission = "msgs.broadcast_list"

def derive_queryset(self, **kwargs):
qs = super().derive_queryset(**kwargs)
Expand Down
2 changes: 1 addition & 1 deletion temba/notifications/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.utils.translation import gettext_lazy as _

from temba.orgs.mixins import OrgPermsMixin
from temba.orgs.views.mixins import OrgPermsMixin
from temba.utils.views import SpaMixin

from .mixins import NotificationTargetMixin
Expand Down
1 change: 1 addition & 0 deletions temba/orgs/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .views import * # noqa
125 changes: 125 additions & 0 deletions temba/orgs/views/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from smartmin.views import SmartListView, SmartTemplateView

from django.http import JsonResponse
from django.urls import reverse
from django.utils.text import slugify

from .mixins import OrgPermsMixin


class BaseListView(OrgPermsMixin, SmartListView):
"""
Base list view for objects that belong to the current org
"""

def derive_queryset(self, *args, **kwargs):
queryset = super().derive_queryset(*args, **kwargs)

if not self.request.user.is_authenticated:
return queryset.none() # pragma: no cover
else:
return queryset.filter(org=self.request.org)


class BaseMenuView(OrgPermsMixin, SmartTemplateView):
"""
Base view for the section menus
"""

def create_divider(self):
return {"type": "divider"}

def create_space(self): # pragma: no cover
return {"type": "space"}

def create_section(self, name, items=()): # pragma: no cover
return {"id": slugify(name), "name": name, "type": "section", "items": items}

def create_list(self, name, href, type):
return {"id": name, "href": href, "type": type}

def create_modax_button(self, name, href, icon=None, on_submit=None): # pragma: no cover
menu_item = {"id": slugify(name), "name": name, "type": "modax-button"}
if href:
if href[0] == "/": # pragma: no cover
menu_item["href"] = href
elif self.has_org_perm(href):
menu_item["href"] = reverse(href)

if on_submit:
menu_item["on_submit"] = on_submit

if icon: # pragma: no cover
menu_item["icon"] = icon

if "href" not in menu_item: # pragma: no cover
return None

return menu_item

def create_menu_item(
self,
menu_id=None,
name=None,
icon=None,
avatar=None,
endpoint=None,
href=None,
count=None,
perm=None,
items=[],
inline=False,
bottom=False,
popup=False,
event=None,
posterize=False,
bubble=None,
mobile=False,
):
if perm and not self.has_org_perm(perm): # pragma: no cover
return

menu_item = {"name": name, "inline": inline}
menu_item["id"] = menu_id if menu_id else slugify(name)
menu_item["bottom"] = bottom
menu_item["popup"] = popup
menu_item["avatar"] = avatar
menu_item["posterize"] = posterize
menu_item["event"] = event
menu_item["mobile"] = mobile

if bubble:
menu_item["bubble"] = bubble

if icon:
menu_item["icon"] = icon

if count is not None:
menu_item["count"] = count

if endpoint:
if endpoint[0] == "/": # pragma: no cover
menu_item["endpoint"] = endpoint
elif perm or self.has_org_perm(endpoint):
menu_item["endpoint"] = reverse(endpoint)

if href:
if href[0] == "/":
menu_item["href"] = href
elif perm or self.has_org_perm(href):
menu_item["href"] = reverse(href)

if items: # pragma: no cover
menu_item["items"] = [item for item in items if item is not None]

# only include the menu item if we have somewhere to go
if "href" not in menu_item and "endpoint" not in menu_item and not inline and not popup and not event:
return None

return menu_item

def get_menu(self):
return [item for item in self.derive_menu() if item is not None]

def render_to_response(self, context, **response_kwargs):
return JsonResponse({"results": self.get_menu()})
Loading

0 comments on commit b3e9a49

Please sign in to comment.