Skip to content

Commit ca94e89

Browse files
committed
Unify feature check for organization/project
1 parent b5908b0 commit ca94e89

File tree

3 files changed

+70
-60
lines changed

3 files changed

+70
-60
lines changed

readthedocs/core/mixins.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from vanilla import ListView
77

88
from readthedocs.projects.models import Feature
9+
from readthedocs.subscriptions.models import PlanFeature
910

1011

1112
class ListViewWithForm(ListView):
@@ -67,3 +68,27 @@ def _is_cache_enabled(self, project):
6768
"""Helper function to check if CND is enabled for a project."""
6869
# TODO: check for the organization's plan.
6970
return settings.ALLOW_PRIVATE_REPOS and project.has_feature(Feature.CDN_ENABLED)
71+
72+
73+
class FeaturedView:
74+
75+
feature_type = None
76+
77+
def _get_feature(self, obj):
78+
feature = PlanFeature.objects.get_feature(
79+
obj=obj,
80+
type=self.feature_type,
81+
)
82+
return feature
83+
84+
def _get_feature_value(self, obj, default=None):
85+
if settings.RTD_ALLOW_ORGANIZATIONS:
86+
feature = self._get_feature(obj)
87+
if feature:
88+
return feature.value
89+
return default
90+
91+
def _is_enabled(self, obj):
92+
if settings.RTD_ALLOW_ORGANIZATIONS:
93+
return self._get_feature(obj) is not None
94+
return True

readthedocs/organizations/views/private.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
from readthedocs.audit.filters import OrganizationSecurityLogFilter
1212
from readthedocs.audit.models import AuditLog
1313
from readthedocs.core.history import UpdateChangeReasonPostView
14-
from readthedocs.core.mixins import PrivateViewMixin
15-
from readthedocs.core.utils.extend import SettingsOverrideObject
14+
from readthedocs.core.mixins import FeaturedView, PrivateViewMixin
1615
from readthedocs.organizations.forms import (
1716
OrganizationSignupForm,
1817
OrganizationTeamProjectForm,
@@ -26,6 +25,7 @@
2625
OrganizationView,
2726
)
2827
from readthedocs.projects.utils import get_csv_file
28+
from readthedocs.subscriptions.models import PlanFeature
2929

3030

3131
# Organization views
@@ -170,12 +170,18 @@ def post(self, request, *args, **kwargs):
170170
return resp
171171

172172

173-
class OrganizationSecurityLogBase(PrivateViewMixin, OrganizationMixin, ListView):
173+
class OrganizationSecurityLog(
174+
FeaturedView,
175+
PrivateViewMixin,
176+
OrganizationMixin,
177+
ListView,
178+
):
174179

175180
"""Display security logs related to this organization."""
176181

177182
model = AuditLog
178183
template_name = 'organizations/security_log.html'
184+
feature_type = PlanFeature.TYPE_AUDIT_LOGS
179185

180186
def get(self, request, *args, **kwargs):
181187
download_data = request.GET.get('download', False)
@@ -273,14 +279,9 @@ def get_queryset(self):
273279
)
274280
return self.filter.qs
275281

276-
def _get_retention_days_limit(self, organization): # noqa
277-
"""From how many days we need to show data for this project?"""
278-
return settings.RTD_AUDITLOGS_DEFAULT_RETENTION_DAYS
279-
280-
def _is_enabled(self, organization): # noqa
281-
"""Should we show audit logs for this organization?"""
282-
return True
283-
284-
285-
class OrganizationSecurityLog(SettingsOverrideObject):
286-
_default_class = OrganizationSecurityLogBase
282+
def _get_retention_days_limit(self, organization):
283+
"""From how many days we need to show data for this organization?"""
284+
return self._get_feature_value(
285+
organization,
286+
default=settings.RTD_AUDITLOGS_DEFAULT_RETENTION_DAYS,
287+
)

readthedocs/projects/views/private.py

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Project views for authenticated users."""
22

33
import structlog
4-
54
from allauth.socialaccount.models import SocialAccount
65
from django.conf import settings
76
from django.contrib import messages
@@ -39,8 +38,7 @@
3938
VersionAutomationRule,
4039
)
4140
from readthedocs.core.history import UpdateChangeReasonPostView
42-
from readthedocs.core.mixins import ListViewWithForm, PrivateViewMixin
43-
from readthedocs.core.utils.extend import SettingsOverrideObject
41+
from readthedocs.core.mixins import ListViewWithForm, PrivateViewMixin, FeaturedView
4442
from readthedocs.integrations.models import HttpExchange, Integration
4543
from readthedocs.oauth.services import registry
4644
from readthedocs.oauth.tasks import attach_webhook
@@ -79,6 +77,8 @@
7977
ProjectRelationListMixin,
8078
)
8179
from readthedocs.search.models import SearchQuery
80+
from readthedocs.subscriptions.models import PlanFeature
81+
8282

8383
log = structlog.get_logger(__name__)
8484

@@ -743,10 +743,11 @@ def post(self, request, *args, **kwargs):
743743
return HttpResponseRedirect(self.get_success_url())
744744

745745

746-
class DomainMixin(ProjectAdminMixin, PrivateViewMixin):
746+
class DomainMixin(FeaturedView, ProjectAdminMixin, PrivateViewMixin):
747747
model = Domain
748748
form_class = DomainForm
749749
lookup_url_kwarg = 'domain_pk'
750+
feature_type = PlanFeature.TYPE_CNAME
750751

751752
def get_success_url(self):
752753
return reverse('projects_domains', args=[self.get_project().slug])
@@ -757,12 +758,8 @@ def get_context_data(self, **kwargs):
757758
context['enabled'] = self._is_enabled(project)
758759
return context
759760

760-
def _is_enabled(self, project):
761-
"""Should we allow custom domains for this project?"""
762-
return True
763-
764761

765-
class DomainListBase(DomainMixin, ListViewWithForm):
762+
class DomainList(DomainMixin, ListViewWithForm):
766763

767764
def get_context_data(self, **kwargs):
768765
ctx = super().get_context_data(**kwargs)
@@ -777,12 +774,7 @@ def get_context_data(self, **kwargs):
777774
return ctx
778775

779776

780-
class DomainList(SettingsOverrideObject):
781-
782-
_default_class = DomainListBase
783-
784-
785-
class DomainCreateBase(DomainMixin, CreateView):
777+
class DomainCreate(DomainMixin, CreateView):
786778

787779
def post(self, request, *args, **kwargs):
788780
project = self.get_project()
@@ -801,12 +793,7 @@ def get_success_url(self):
801793
)
802794

803795

804-
class DomainCreate(SettingsOverrideObject):
805-
806-
_default_class = DomainCreateBase
807-
808-
809-
class DomainUpdateBase(DomainMixin, UpdateView):
796+
class DomainUpdate(DomainMixin, UpdateView):
810797

811798
def post(self, request, *args, **kwargs):
812799
project = self.get_project()
@@ -815,11 +802,6 @@ def post(self, request, *args, **kwargs):
815802
return HttpResponse('Action not allowed', status=401)
816803

817804

818-
class DomainUpdate(SettingsOverrideObject):
819-
820-
_default_class = DomainUpdateBase
821-
822-
823805
class DomainDelete(DomainMixin, DeleteView):
824806

825807
pass
@@ -1062,10 +1044,16 @@ class RegexAutomationRuleUpdate(RegexAutomationRuleMixin, UpdateView):
10621044
pass
10631045

10641046

1065-
class SearchAnalyticsBase(ProjectAdminMixin, PrivateViewMixin, TemplateView):
1047+
class SearchAnalytics(
1048+
FeaturedView,
1049+
ProjectAdminMixin,
1050+
PrivateViewMixin,
1051+
TemplateView,
1052+
):
10661053

10671054
template_name = 'projects/projects_search_analytics.html'
10681055
http_method_names = ['get']
1056+
feature_type = PlanFeature.TYPE_SEARCH_ANALYTICS
10691057

10701058
def get(self, request, *args, **kwargs):
10711059
download_data = request.GET.get('download', False)
@@ -1149,21 +1137,22 @@ def _get_csv_data(self):
11491137

11501138
def _get_retention_days_limit(self, project):
11511139
"""From how many days we need to show data for this project?"""
1152-
return settings.RTD_ANALYTICS_DEFAULT_RETENTION_DAYS
1153-
1154-
def _is_enabled(self, project):
1155-
"""Should we show search analytics for this project?"""
1156-
return True
1157-
1158-
1159-
class SearchAnalytics(SettingsOverrideObject):
1160-
_default_class = SearchAnalyticsBase
1140+
return self._get_feature_value(
1141+
project,
1142+
default=settings.RTD_ANALYTICS_DEFAULT_RETENTION_DAYS,
1143+
)
11611144

11621145

1163-
class TrafficAnalyticsViewBase(ProjectAdminMixin, PrivateViewMixin, TemplateView):
1146+
class TrafficAnalyticsView(
1147+
FeaturedView,
1148+
ProjectAdminMixin,
1149+
PrivateViewMixin,
1150+
TemplateView,
1151+
):
11641152

11651153
template_name = 'projects/project_traffic_analytics.html'
11661154
http_method_names = ['get']
1155+
feature_type = PlanFeature.TYPE_PAGEVIEW_ANALYTICS
11671156

11681157
def get(self, request, *args, **kwargs):
11691158
download_data = request.GET.get('download', False)
@@ -1239,12 +1228,7 @@ def _get_csv_data(self):
12391228

12401229
def _get_retention_days_limit(self, project):
12411230
"""From how many days we need to show data for this project?"""
1242-
return settings.RTD_ANALYTICS_DEFAULT_RETENTION_DAYS
1243-
1244-
def _is_enabled(self, project):
1245-
"""Should we show traffic analytics for this project?"""
1246-
return True
1247-
1248-
1249-
class TrafficAnalyticsView(SettingsOverrideObject):
1250-
_default_class = TrafficAnalyticsViewBase
1231+
return self._get_feature_value(
1232+
project,
1233+
default=settings.RTD_ANALYTICS_DEFAULT_RETENTION_DAYS,
1234+
)

0 commit comments

Comments
 (0)