Use project contact form handler and superuser-only snippet access
This commit is contained in:
@@ -4,4 +4,3 @@ Ocyan loads contact form handlers via module labels like `contact_form.views`.
|
||||
By shipping this package in the project repository we can extend behavior
|
||||
without forking the upstream plugin.
|
||||
"""
|
||||
|
||||
|
||||
@@ -12,9 +12,10 @@ from django.utils.translation import gettext as _
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from oscar.core.utils import redirect_to_referrer
|
||||
from wagtail.models import Locale, Site
|
||||
|
||||
from oscar.core.utils import redirect_to_referrer
|
||||
|
||||
from ocyan.core.fender import config
|
||||
from ocyan.plugin.contact_form.forms import ContactForm
|
||||
from ocyan.plugin.contact_form.utils import get_from_email, get_to_email
|
||||
@@ -30,6 +31,7 @@ def _client_ip(request) -> str | None:
|
||||
return forwarded_for.split(",")[0].strip() or None
|
||||
return request.META.get("REMOTE_ADDR")
|
||||
|
||||
|
||||
def _active_locale(request) -> Locale:
|
||||
language_code = (getattr(request, "LANGUAGE_CODE", "") or "").split("-")[0]
|
||||
if language_code:
|
||||
@@ -51,7 +53,11 @@ def post_contact_form(request):
|
||||
message_obj = ContactMessage.objects.create(
|
||||
site=site,
|
||||
locale=locale,
|
||||
user=request.user if getattr(request.user, "is_authenticated", False) else None,
|
||||
user=(
|
||||
request.user
|
||||
if getattr(request.user, "is_authenticated", False)
|
||||
else None
|
||||
),
|
||||
ip_address=_client_ip(request),
|
||||
path=request.path or "",
|
||||
name=str(cleaned.get("name", "")),
|
||||
@@ -59,7 +65,11 @@ def post_contact_form(request):
|
||||
phone_number=str(cleaned.get("phonenumber") or ""),
|
||||
message=str(cleaned.get("message", "")),
|
||||
)
|
||||
logger.info("Saved ContactMessage id=%s email=%s", message_obj.id, message_obj.email)
|
||||
logger.info(
|
||||
"Saved ContactMessage id=%s email=%s",
|
||||
message_obj.id,
|
||||
message_obj.email,
|
||||
)
|
||||
|
||||
context = {
|
||||
"website_url": request.build_absolute_uri(),
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def grant_contactmessage_permissions(apps, schema_editor):
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
ContentType = apps.get_model("contenttypes", "ContentType")
|
||||
|
||||
try:
|
||||
group = Group.objects.get(name="Editors")
|
||||
except Group.DoesNotExist:
|
||||
return
|
||||
|
||||
content_type = ContentType.objects.get(app_label="mandelstudio", model="contactmessage")
|
||||
perms = Permission.objects.filter(
|
||||
content_type=content_type,
|
||||
codename__in=[
|
||||
"add_contactmessage",
|
||||
"change_contactmessage",
|
||||
"delete_contactmessage",
|
||||
"view_contactmessage",
|
||||
],
|
||||
)
|
||||
group.permissions.add(*perms)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("mandelstudio", "0004_contact_messages"),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
grant_contactmessage_permissions,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def grant_contactmessage_permissions_to_staff(apps, schema_editor):
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
ContentType = apps.get_model("contenttypes", "ContentType")
|
||||
|
||||
# Default Django user model in this project.
|
||||
User = apps.get_model("auth", "User")
|
||||
|
||||
content_type = ContentType.objects.get(app_label="mandelstudio", model="contactmessage")
|
||||
perms = list(
|
||||
Permission.objects.filter(
|
||||
content_type=content_type,
|
||||
codename__in=[
|
||||
"add_contactmessage",
|
||||
"change_contactmessage",
|
||||
"delete_contactmessage",
|
||||
"view_contactmessage",
|
||||
],
|
||||
)
|
||||
)
|
||||
if not perms:
|
||||
return
|
||||
|
||||
for user in User.objects.filter(is_staff=True, is_active=True).iterator():
|
||||
user.user_permissions.add(*perms)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("mandelstudio", "0005_grant_contactmessage_permissions"),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
grant_contactmessage_permissions_to_staff,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def remove_contactmessage_permissions_from_editors(apps, schema_editor):
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
ContentType = apps.get_model("contenttypes", "ContentType")
|
||||
|
||||
try:
|
||||
group = Group.objects.get(name="Editors")
|
||||
except Group.DoesNotExist:
|
||||
return
|
||||
|
||||
content_type = ContentType.objects.get(app_label="mandelstudio", model="contactmessage")
|
||||
perms = Permission.objects.filter(
|
||||
content_type=content_type,
|
||||
codename__in=[
|
||||
"add_contactmessage",
|
||||
"change_contactmessage",
|
||||
"delete_contactmessage",
|
||||
"view_contactmessage",
|
||||
],
|
||||
)
|
||||
group.permissions.remove(*perms)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("mandelstudio", "0006_grant_contactmessage_permissions_to_staff"),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
remove_contactmessage_permissions_from_editors,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
||||
|
||||
37
mandelstudio/tests/test_contact_form_routing.py
Normal file
37
mandelstudio/tests/test_contact_form_routing.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import SimpleTestCase, TestCase
|
||||
from django.urls import resolve
|
||||
|
||||
from ocyan.plugin.contact_form.entrypoint import SHOP_BASE_URL
|
||||
|
||||
from contact_form.views import post_contact_form
|
||||
from mandelstudio.models import ContactMessage
|
||||
from mandelstudio.wagtail_hooks import SuperuserOnlyPermissionPolicy
|
||||
|
||||
|
||||
class ContactFormRoutingTests(SimpleTestCase):
|
||||
def test_shop_contact_form_uses_project_handler(self):
|
||||
match = resolve(f"/{SHOP_BASE_URL}/contact-form/")
|
||||
|
||||
self.assertIs(match.func, post_contact_form)
|
||||
|
||||
|
||||
class ContactMessagePermissionPolicyTests(TestCase):
|
||||
def test_only_superusers_have_contact_message_permissions(self):
|
||||
user_model = get_user_model()
|
||||
superuser = user_model.objects.create_superuser(
|
||||
"contact-superuser",
|
||||
"superuser@example.com",
|
||||
"password",
|
||||
)
|
||||
staff_user = user_model.objects.create_user(
|
||||
"contact-staff",
|
||||
"staff@example.com",
|
||||
"password",
|
||||
is_staff=True,
|
||||
)
|
||||
policy = SuperuserOnlyPermissionPolicy(ContactMessage)
|
||||
|
||||
self.assertTrue(policy.user_has_permission(superuser, "view"))
|
||||
self.assertFalse(policy.user_has_permission(staff_user, "view"))
|
||||
self.assertFalse(policy.user_has_any_permission(staff_user, {"view", "change"}))
|
||||
@@ -1,9 +1,14 @@
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.urls import path
|
||||
from django.views.decorators.cache import cache_page
|
||||
|
||||
from ocyan.core.fender import config
|
||||
from ocyan.main.urls import urlpatterns as ocyan_urlpatterns
|
||||
from ocyan.plugin.contact_form.entrypoint import SHOP_BASE_URL
|
||||
from ocyan.plugin.wagtail_oscar_integration.constants import CACHE_DURATION
|
||||
|
||||
from contact_form.views import post_contact_form
|
||||
|
||||
from .i18n_views import set_language_normalized
|
||||
from .sitemaps import robots_txt, sitemap_index, sitemap_section
|
||||
|
||||
@@ -22,4 +27,20 @@ urlpatterns = [
|
||||
),
|
||||
]
|
||||
|
||||
contact_form_urlpatterns = [
|
||||
path(
|
||||
f"{SHOP_BASE_URL}/contact-form/",
|
||||
post_contact_form,
|
||||
name="project-contact-form-handler",
|
||||
),
|
||||
]
|
||||
|
||||
if config.i18n_enabled:
|
||||
urlpatterns += i18n_patterns(
|
||||
*contact_form_urlpatterns,
|
||||
prefix_default_language=False,
|
||||
)
|
||||
else:
|
||||
urlpatterns += contact_form_urlpatterns
|
||||
|
||||
urlpatterns += ocyan_urlpatterns
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from wagtail.permissions import ModelPermissionPolicy
|
||||
from wagtail.snippets.models import register_snippet
|
||||
from wagtail.snippets.views.snippets import SnippetViewSet
|
||||
|
||||
@@ -5,6 +8,40 @@ from mandelblog_content_guard.hooks import * # noqa: F401,F403
|
||||
from mandelstudio.models import ContactMessage
|
||||
|
||||
|
||||
class SuperuserOnlyPermissionPolicy(ModelPermissionPolicy):
|
||||
def user_has_permission(self, user, action):
|
||||
return user.is_active and user.is_superuser
|
||||
|
||||
def user_has_any_permission(self, user, actions):
|
||||
return user.is_active and user.is_superuser
|
||||
|
||||
def user_has_permission_for_instance(self, user, action, instance):
|
||||
return self.user_has_permission(user, action)
|
||||
|
||||
def user_has_any_permission_for_instance(self, user, actions, instance):
|
||||
return self.user_has_any_permission(user, actions)
|
||||
|
||||
def instances_user_has_any_permission_for(self, user, actions):
|
||||
if self.user_has_any_permission(user, actions):
|
||||
return self.model._default_manager.all()
|
||||
return self.model._default_manager.none()
|
||||
|
||||
def instances_user_has_permission_for(self, user, action):
|
||||
return self.instances_user_has_any_permission_for(user, [action])
|
||||
|
||||
def users_with_any_permission(self, actions):
|
||||
return get_user_model().objects.filter(is_active=True, is_superuser=True)
|
||||
|
||||
def users_with_permission(self, action):
|
||||
return self.users_with_any_permission([action])
|
||||
|
||||
def users_with_any_permission_for_instance(self, actions, instance):
|
||||
return self.users_with_any_permission(actions)
|
||||
|
||||
def users_with_permission_for_instance(self, action, instance):
|
||||
return self.users_with_any_permission_for_instance([action], instance)
|
||||
|
||||
|
||||
@register_snippet
|
||||
class ContactMessageViewSet(SnippetViewSet):
|
||||
model = ContactMessage
|
||||
@@ -19,3 +56,7 @@ class ContactMessageViewSet(SnippetViewSet):
|
||||
list_filter = ("locale", "site")
|
||||
search_fields = ("name", "email", "message", "phone_number")
|
||||
ordering = ("-created_at",)
|
||||
|
||||
@property
|
||||
def permission_policy(self):
|
||||
return SuperuserOnlyPermissionPolicy(self.model)
|
||||
|
||||
Reference in New Issue
Block a user