Plain is headed towards 1.0! Subscribe for development updates →

 1from __future__ import annotations
 2
 3from typing import Any
 4
 5from plain.assets.urls import get_asset_url
 6from plain.auth.views import AuthViewMixin
 7from plain.forms import Form
 8from plain.http import Response, ResponseRedirect
 9from plain.runtime import settings
10from plain.utils.module_loading import import_string
11from plain.views import FormView, View
12
13
14class SupportFormView(AuthViewMixin, FormView):
15    template_name = "support/page.html"
16
17    def get_form(self) -> Form:
18        form_slug = self.url_kwargs["form_slug"]
19        form_class = import_string(settings.SUPPORT_FORMS[form_slug])
20        return form_class(**self.get_form_kwargs())
21
22    def get_template_context(self) -> dict[str, Any]:
23        context = super().get_template_context()
24        form_slug = self.url_kwargs["form_slug"]
25        context["form_action"] = self.request.build_absolute_uri()
26        context["form_template_name"] = f"support/forms/{form_slug}.html"
27        context["success_template_name"] = f"support/success/{form_slug}.html"
28        context["success"] = self.request.query_params.get("success") == "true"
29        return context
30
31    def get_form_kwargs(self) -> dict[str, Any]:
32        kwargs = super().get_form_kwargs()
33        kwargs["user"] = self.user
34        kwargs["form_slug"] = self.url_kwargs["form_slug"]
35        return kwargs
36
37    def form_valid(self, form: Any) -> Response:
38        entry = form.save()
39        form.notify(entry)
40        return super().form_valid(form)
41
42    def get_success_url(self, form: Any) -> str:
43        # Redirect to the same view and template so we
44        # don't have to create two additional views for iframe and non-iframe.
45        return "?success=true"
46
47
48class SupportIFrameView(SupportFormView):
49    template_name = "support/iframe.html"
50
51    def get_response(self) -> Response:
52        response = super().get_response()
53
54        # X-Frame-Options are typically in DEFAULT_RESPONSE_HEADERS,
55        # which will know to drop the header completely if an empty string.
56        # We can't del/pop it because DEFAULT_RESPONSE_HEADERS may add it back.
57        response.headers["X-Frame-Options"] = ""
58
59        return response
60
61
62class SupportFormJSView(View):
63    def get(self) -> ResponseRedirect:
64        return ResponseRedirect(get_asset_url("support/embed.js"))