kopia lustrzana https://github.com/wagtail/wagtail
808 wiersze
29 KiB
Python
808 wiersze
29 KiB
Python
import re
|
|
import urllib.parse as urlparse
|
|
|
|
from django.conf import settings
|
|
from django.core.paginator import InvalidPage, Paginator
|
|
from django.http import Http404
|
|
from django.shortcuts import get_object_or_404
|
|
from django.template.response import TemplateResponse
|
|
from django.urls.base import reverse
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.views.generic.base import View
|
|
|
|
from wagtail import hooks
|
|
from wagtail.admin.forms.choosers import (
|
|
AnchorLinkChooserForm,
|
|
EmailLinkChooserForm,
|
|
ExternalLinkChooserForm,
|
|
PhoneLinkChooserForm,
|
|
)
|
|
from wagtail.admin.forms.search import SearchForm
|
|
from wagtail.admin.modal_workflow import render_modal_workflow
|
|
from wagtail.admin.ui.tables import Column, DateColumn, Table
|
|
from wagtail.coreutils import resolve_model_string
|
|
from wagtail.models import Locale, Page, Site
|
|
|
|
|
|
def shared_context(request, extra_context=None):
|
|
context = {
|
|
# parent_page ID is passed as a GET parameter on the external_link, anchor_link and mail_link views
|
|
# so that it's remembered when browsing from 'Internal link' to another link type
|
|
# and back again. On the 'browse' / 'internal link' view this will be overridden to be
|
|
# sourced from the standard URL path parameter instead.
|
|
"parent_page_id": request.GET.get("parent_page_id"),
|
|
"allow_external_link": request.GET.get("allow_external_link"),
|
|
"allow_email_link": request.GET.get("allow_email_link"),
|
|
"allow_phone_link": request.GET.get("allow_phone_link"),
|
|
"allow_anchor_link": request.GET.get("allow_anchor_link"),
|
|
}
|
|
if extra_context:
|
|
context.update(extra_context)
|
|
return context
|
|
|
|
|
|
def page_models_from_string(string):
|
|
page_models = []
|
|
|
|
for sub_string in string.split(","):
|
|
page_model = resolve_model_string(sub_string)
|
|
|
|
if not issubclass(page_model, Page):
|
|
raise ValueError("Model is not a page")
|
|
|
|
page_models.append(page_model)
|
|
|
|
return tuple(page_models)
|
|
|
|
|
|
def can_choose_page(
|
|
page,
|
|
user,
|
|
desired_classes,
|
|
can_choose_root=True,
|
|
user_perm=None,
|
|
target_pages=None,
|
|
match_subclass=True,
|
|
):
|
|
"""Returns boolean indicating of the user can choose page.
|
|
will check if the root page can be selected and if user permissions
|
|
should be checked.
|
|
"""
|
|
|
|
if not target_pages:
|
|
target_pages = []
|
|
|
|
if not match_subclass and page.specific_class not in desired_classes:
|
|
return False
|
|
elif (
|
|
match_subclass
|
|
and not issubclass(page.specific_class or Page, desired_classes)
|
|
and not desired_classes == (Page,)
|
|
):
|
|
return False
|
|
elif not can_choose_root and page.is_root():
|
|
return False
|
|
|
|
if user_perm in ["move_to", "bulk_move_to"]:
|
|
pages_to_move = target_pages
|
|
|
|
for page_to_move in pages_to_move:
|
|
if page.pk == page_to_move.pk or page.is_descendant_of(page_to_move):
|
|
return False
|
|
|
|
if user_perm == "move_to":
|
|
return page_to_move.permissions_for_user(user).can_move_to(page)
|
|
if user_perm in {"add_subpage", "copy_to"}:
|
|
return page.permissions_for_user(user).can_add_subpage()
|
|
|
|
return True
|
|
|
|
|
|
class PageChooserTable(Table):
|
|
classname = "listing chooser"
|
|
|
|
def __init__(self, *args, show_locale_labels=False, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.show_locale_labels = show_locale_labels
|
|
|
|
def get_context_data(self, parent_context):
|
|
context = super().get_context_data(parent_context)
|
|
context["show_locale_labels"] = self.show_locale_labels
|
|
return context
|
|
|
|
def get_row_classname(self, page):
|
|
classnames = []
|
|
if page.is_parent_page:
|
|
classnames.append("parent-page")
|
|
if not page.live:
|
|
classnames.append("unpublished")
|
|
if not page.can_choose:
|
|
classnames.append("disabled")
|
|
|
|
return " ".join(classnames)
|
|
|
|
|
|
class PageTitleColumn(Column):
|
|
cell_template_name = "wagtailadmin/chooser/tables/page_title_cell.html"
|
|
|
|
def __init__(self, *args, is_multiple_choice=False, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.is_multiple_choice = is_multiple_choice
|
|
|
|
def get_value(self, instance):
|
|
return instance.get_admin_display_title()
|
|
|
|
def get_cell_context_data(self, instance, parent_context):
|
|
context = super().get_cell_context_data(instance, parent_context)
|
|
context["page"] = instance
|
|
# only need to show locale labels for top-level pages
|
|
context["show_locale_labels"] = (
|
|
parent_context.get("show_locale_labels") and instance.depth == 2
|
|
)
|
|
return context
|
|
|
|
|
|
class ParentPageColumn(Column):
|
|
cell_template_name = "wagtailadmin/chooser/tables/parent_page_cell.html"
|
|
|
|
def get_value(self, instance):
|
|
return instance.get_parent()
|
|
|
|
def get_cell_context_data(self, instance, parent_context):
|
|
context = super().get_cell_context_data(instance, parent_context)
|
|
context["show_locale_labels"] = parent_context.get("show_locale_labels")
|
|
return context
|
|
|
|
|
|
class PageStatusColumn(Column):
|
|
cell_template_name = "wagtailadmin/chooser/tables/page_status_cell.html"
|
|
|
|
def get_value(self, instance):
|
|
return instance
|
|
|
|
|
|
class PageNavigateToChildrenColumn(Column):
|
|
cell_template_name = (
|
|
"wagtailadmin/chooser/tables/page_navigate_to_children_cell.html"
|
|
)
|
|
|
|
def get_value(self, instance):
|
|
return instance
|
|
|
|
|
|
class PageCheckboxSelectColumn(Column):
|
|
cell_template_name = "wagtailadmin/chooser/tables/page_checkbox_select_cell.html"
|
|
|
|
|
|
class BrowseView(View):
|
|
@property
|
|
def columns(self):
|
|
cols = [
|
|
PageTitleColumn(
|
|
"title",
|
|
label=_("Title"),
|
|
is_multiple_choice=self.is_multiple_choice,
|
|
),
|
|
DateColumn(
|
|
"updated",
|
|
label=_("Updated"),
|
|
width="12%",
|
|
accessor="latest_revision_created_at",
|
|
),
|
|
Column(
|
|
"type",
|
|
label=_("Type"),
|
|
width="12%",
|
|
accessor="page_type_display_name",
|
|
),
|
|
PageStatusColumn("status", label=_("Status"), width="12%"),
|
|
PageNavigateToChildrenColumn("children", label="", width="10%"),
|
|
]
|
|
if self.is_multiple_choice:
|
|
cols.insert(
|
|
0,
|
|
PageCheckboxSelectColumn(
|
|
"select", label=_("Select"), width="1%", accessor="pk"
|
|
),
|
|
)
|
|
return cols
|
|
|
|
def get_object_list(self):
|
|
# Get children of parent page (without streamfields)
|
|
pages = self.parent_page.get_children().defer_streamfields().specific()
|
|
if self.i18n_enabled:
|
|
pages = pages.select_related("locale")
|
|
return pages
|
|
|
|
def filter_object_list(self, pages):
|
|
# allow hooks to modify the queryset
|
|
for hook in hooks.get_hooks("construct_page_chooser_queryset"):
|
|
pages = hook(pages, self.request)
|
|
|
|
# Filter them by page type
|
|
if self.desired_classes != (Page,):
|
|
# restrict the page listing to just those pages that:
|
|
# - are of the given content type (taking into account class inheritance)
|
|
# - or can be navigated into (i.e. have children)
|
|
choosable_pages = pages.type(*self.desired_classes)
|
|
descendable_pages = pages.filter(numchild__gt=0)
|
|
pages = choosable_pages | descendable_pages
|
|
|
|
return pages
|
|
|
|
def get(self, request, parent_page_id=None):
|
|
self.i18n_enabled = getattr(settings, "WAGTAIL_I18N_ENABLED", False)
|
|
self.is_multiple_choice = request.GET.get("multiple")
|
|
|
|
# A missing or empty page_type parameter indicates 'all page types'
|
|
# (i.e. descendants of wagtailcore.page)
|
|
page_type_string = request.GET.get("page_type") or "wagtailcore.page"
|
|
user_perm = request.GET.get("user_perms", False)
|
|
|
|
try:
|
|
self.desired_classes = page_models_from_string(page_type_string)
|
|
except (ValueError, LookupError):
|
|
raise Http404
|
|
|
|
# Find parent page
|
|
if parent_page_id:
|
|
self.parent_page = get_object_or_404(Page, id=parent_page_id)
|
|
elif self.desired_classes == (Page,):
|
|
# Just use the root page
|
|
self.parent_page = Page.get_first_root_node()
|
|
else:
|
|
# Find the highest common ancestor for the specific classes passed in
|
|
# In many cases, such as selecting an EventPage under an EventIndex,
|
|
# this will help the administrator find their page quicker.
|
|
all_desired_pages = Page.objects.all().type(*self.desired_classes)
|
|
self.parent_page = all_desired_pages.first_common_ancestor()
|
|
|
|
self.parent_page = self.parent_page.specific
|
|
|
|
pages = self.get_object_list()
|
|
pages = self.filter_object_list(pages)
|
|
|
|
can_choose_root = request.GET.get("can_choose_root", False)
|
|
target_pages = Page.objects.filter(
|
|
pk__in=[int(pk) for pk in request.GET.getlist("target_pages[]", []) if pk]
|
|
)
|
|
|
|
match_subclass = request.GET.get("match_subclass", True)
|
|
|
|
# Parent page can be chosen if it is a instance of desired_classes
|
|
self.parent_page.can_choose = can_choose_page(
|
|
self.parent_page,
|
|
request.user,
|
|
self.desired_classes,
|
|
can_choose_root,
|
|
user_perm,
|
|
target_pages=target_pages,
|
|
match_subclass=match_subclass,
|
|
)
|
|
self.parent_page.is_parent_page = True
|
|
self.parent_page.can_descend = False
|
|
|
|
selected_locale = None
|
|
locale_options = []
|
|
if self.i18n_enabled:
|
|
# Ensure query parameters (e.g. `page_type`, `user_perms`, etc.) are
|
|
# preserved when switching locales, but reset the pagination as the
|
|
# number of pages might be different.
|
|
new_params = request.GET.copy()
|
|
new_params.pop("p", None)
|
|
if self.parent_page.is_root():
|
|
# 'locale' is the current value of the "Locale" selector in the UI
|
|
if request.GET.get("locale"):
|
|
selected_locale = get_object_or_404(
|
|
Locale, language_code=request.GET["locale"]
|
|
)
|
|
elif request.GET.get("instance_id"):
|
|
page_instance = Page.objects.get(id=request.GET["instance_id"])
|
|
selected_locale = page_instance.locale
|
|
else:
|
|
selected_locale = Locale.get_active()
|
|
active_locale_id = selected_locale.pk
|
|
|
|
# we are at the Root level, so get the locales from the current pages
|
|
choose_url = reverse("wagtailadmin_choose_page")
|
|
for locale in Locale.objects.filter(
|
|
pk__in=pages.values_list("locale_id")
|
|
).exclude(pk=active_locale_id):
|
|
new_params["locale"] = locale.language_code
|
|
locale_options.append(
|
|
{
|
|
"locale": locale,
|
|
"url": choose_url + "?" + new_params.urlencode(),
|
|
}
|
|
)
|
|
else:
|
|
# We have a parent page (that is not the root page). Use its locale as the selected localer
|
|
selected_locale = self.parent_page.locale
|
|
new_params.pop("locale", None)
|
|
# and get the locales based on its available translations
|
|
locales_and_parent_pages = {
|
|
item["locale"]: item["pk"]
|
|
for item in Page.objects.translation_of(self.parent_page).values(
|
|
"locale", "pk"
|
|
)
|
|
}
|
|
locales_and_parent_pages[selected_locale.pk] = self.parent_page.pk
|
|
for locale in Locale.objects.filter(
|
|
pk__in=list(locales_and_parent_pages.keys())
|
|
).exclude(pk=selected_locale.pk):
|
|
choose_child_url = reverse(
|
|
"wagtailadmin_choose_page_child",
|
|
args=[locales_and_parent_pages[locale.pk]],
|
|
)
|
|
locale_options.append(
|
|
{
|
|
"locale": locale,
|
|
"url": choose_child_url + "?" + new_params.urlencode(),
|
|
}
|
|
)
|
|
|
|
# finally, filter the browsable pages on the selected locale
|
|
if selected_locale:
|
|
pages = pages.filter(locale=selected_locale)
|
|
|
|
# Pagination
|
|
# We apply pagination first so we don't need to walk the entire list
|
|
# in the block below
|
|
paginator = Paginator(pages, per_page=25)
|
|
try:
|
|
pages = paginator.page(request.GET.get("p", 1))
|
|
except InvalidPage:
|
|
raise Http404
|
|
|
|
# Annotate each page with can_choose/can_descend flags
|
|
for page in pages:
|
|
page.can_choose = can_choose_page(
|
|
page,
|
|
request.user,
|
|
self.desired_classes,
|
|
can_choose_root,
|
|
user_perm,
|
|
target_pages=target_pages,
|
|
match_subclass=match_subclass,
|
|
)
|
|
page.can_descend = page.get_children_count()
|
|
page.is_parent_page = False
|
|
|
|
table = PageChooserTable(
|
|
self.columns,
|
|
[self.parent_page] + list(pages),
|
|
show_locale_labels=self.i18n_enabled,
|
|
)
|
|
|
|
# Render
|
|
context = shared_context(
|
|
request,
|
|
{
|
|
"parent_page": self.parent_page,
|
|
"parent_page_id": self.parent_page.pk,
|
|
"table": table,
|
|
"pagination_page": pages,
|
|
"search_form": SearchForm(),
|
|
"page_type_string": page_type_string,
|
|
"page_type_names": [
|
|
desired_class.get_verbose_name()
|
|
for desired_class in self.desired_classes
|
|
],
|
|
"page_types_restricted": (page_type_string != "wagtailcore.page"),
|
|
"show_locale_controls": self.i18n_enabled,
|
|
"locale_options": locale_options,
|
|
"selected_locale": selected_locale,
|
|
"is_multiple_choice": self.is_multiple_choice,
|
|
},
|
|
)
|
|
|
|
return render_modal_workflow(
|
|
request,
|
|
"wagtailadmin/chooser/browse.html",
|
|
None,
|
|
context,
|
|
json_data={"step": "browse", "parent_page_id": context["parent_page_id"]},
|
|
)
|
|
|
|
|
|
class SearchView(View):
|
|
@property
|
|
def columns(self):
|
|
cols = [
|
|
PageTitleColumn("title", label=_("Title")),
|
|
ParentPageColumn("parent", label=_("Parent")),
|
|
DateColumn(
|
|
"updated",
|
|
label=_("Updated"),
|
|
width="12%",
|
|
accessor="latest_revision_created_at",
|
|
),
|
|
Column(
|
|
"type",
|
|
label=_("Type"),
|
|
width="12%",
|
|
accessor="page_type_display_name",
|
|
),
|
|
PageStatusColumn("status", label=_("Status"), width="12%"),
|
|
]
|
|
if self.is_multiple_choice:
|
|
cols.insert(
|
|
0,
|
|
PageCheckboxSelectColumn(
|
|
"select", label=_("Select"), width="1%", accessor="pk"
|
|
),
|
|
)
|
|
return cols
|
|
|
|
def get(self, request):
|
|
self.i18n_enabled = getattr(settings, "WAGTAIL_I18N_ENABLED", False)
|
|
self.is_multiple_choice = request.GET.get("multiple")
|
|
# A missing or empty page_type parameter indicates 'all page types' (i.e. descendants of wagtailcore.page)
|
|
page_type_string = request.GET.get("page_type") or "wagtailcore.page"
|
|
|
|
try:
|
|
desired_classes = page_models_from_string(page_type_string)
|
|
except (ValueError, LookupError):
|
|
raise Http404
|
|
|
|
pages = Page.objects.all()
|
|
if self.i18n_enabled:
|
|
pages = pages.select_related("locale")
|
|
|
|
# allow hooks to modify the queryset
|
|
for hook in hooks.get_hooks("construct_page_chooser_queryset"):
|
|
pages = hook(pages, request)
|
|
|
|
search_form = SearchForm(request.GET)
|
|
if search_form.is_valid() and search_form.cleaned_data["q"]:
|
|
pages = pages.exclude(depth=1) # never include root
|
|
pages = pages.type(*desired_classes)
|
|
pages = pages.specific()
|
|
pages = pages.autocomplete(search_form.cleaned_data["q"])
|
|
|
|
else:
|
|
pages = pages.none()
|
|
|
|
paginator = Paginator(pages, per_page=25)
|
|
pages = paginator.get_page(request.GET.get("p"))
|
|
|
|
for page in pages:
|
|
page.can_choose = True
|
|
page.is_parent_page = False
|
|
|
|
table = PageChooserTable(
|
|
self.columns,
|
|
pages,
|
|
show_locale_labels=self.i18n_enabled,
|
|
)
|
|
|
|
return TemplateResponse(
|
|
request,
|
|
"wagtailadmin/chooser/_search_results.html",
|
|
shared_context(
|
|
request,
|
|
{
|
|
"searchform": search_form,
|
|
"table": table,
|
|
"pages": pages,
|
|
"page_type_string": page_type_string,
|
|
},
|
|
),
|
|
)
|
|
|
|
|
|
class ChosenMultipleView(View):
|
|
"""
|
|
A view that takes a list of 'id' URL parameters and returns a modal workflow response indicating
|
|
that those objects have been chosen
|
|
"""
|
|
|
|
def render_chosen_response(self, result):
|
|
return render_modal_workflow(
|
|
self.request,
|
|
None,
|
|
None,
|
|
None,
|
|
json_data={"step": "page_chosen", "result": result},
|
|
)
|
|
|
|
def get(self, request):
|
|
pks = request.GET.getlist("id")
|
|
pages = Page.objects.filter(pk__in=pks).specific()
|
|
result = [
|
|
{
|
|
"id": page.pk,
|
|
"parentId": page.get_parent().pk,
|
|
"adminTitle": page.get_admin_display_title(),
|
|
"editUrl": reverse("wagtailadmin_pages:edit", args=(page.pk,)),
|
|
"url": page.url,
|
|
}
|
|
for page in pages
|
|
]
|
|
return self.render_chosen_response(result)
|
|
|
|
|
|
class BaseLinkFormView(View):
|
|
def get_initial_data(self):
|
|
return {
|
|
self.link_url_field_name: self.request.GET.get("link_url", ""),
|
|
"link_text": self.request.GET.get("link_text", ""),
|
|
}
|
|
|
|
def get_url_from_field_value(self, value):
|
|
return value
|
|
|
|
def get_result_data(self):
|
|
url_field_value = self.form.cleaned_data[self.link_url_field_name]
|
|
return {
|
|
"url": self.get_url_from_field_value(url_field_value),
|
|
"title": self.form.cleaned_data["link_text"].strip() or url_field_value,
|
|
# If the user has explicitly entered / edited something in the link_text field,
|
|
# always use that text. If not, we should favour keeping the existing link/selection
|
|
# text, where applicable.
|
|
# (Normally this will match the link_text passed in the URL here anyhow,
|
|
# but that won't account for non-text content such as images.)
|
|
"prefer_this_title_as_link_text": ("link_text" in self.form.changed_data),
|
|
}
|
|
|
|
def get(self, request):
|
|
self.form = self.form_class(
|
|
initial=self.get_initial_data(), prefix=self.form_prefix
|
|
)
|
|
return self.render_form_response()
|
|
|
|
def post(self, request):
|
|
self.form = self.form_class(
|
|
request.POST, initial=self.get_initial_data(), prefix=self.form_prefix
|
|
)
|
|
|
|
if self.form.is_valid():
|
|
result = self.get_result_data()
|
|
return self.render_chosen_response(result)
|
|
else: # form invalid
|
|
return self.render_form_response()
|
|
|
|
def render_form_response(self):
|
|
return render_modal_workflow(
|
|
self.request,
|
|
self.template_name,
|
|
None,
|
|
shared_context(
|
|
self.request,
|
|
{
|
|
"form": self.form,
|
|
},
|
|
),
|
|
json_data={"step": self.step_name},
|
|
)
|
|
|
|
def render_chosen_response(self, result):
|
|
return render_modal_workflow(
|
|
self.request,
|
|
None,
|
|
None,
|
|
None,
|
|
json_data={"step": "external_link_chosen", "result": result},
|
|
)
|
|
|
|
|
|
LINK_CONVERSION_ALL = "all"
|
|
LINK_CONVERSION_EXACT = "exact"
|
|
LINK_CONVERSION_CONFIRM = "confirm"
|
|
|
|
|
|
class ExternalLinkView(BaseLinkFormView):
|
|
form_prefix = "external-link-chooser"
|
|
form_class = ExternalLinkChooserForm
|
|
template_name = "wagtailadmin/chooser/external_link.html"
|
|
step_name = "external_link"
|
|
link_url_field_name = "url"
|
|
|
|
def post(self, request):
|
|
self.form = self.form_class(
|
|
request.POST,
|
|
initial=self.get_initial_data(),
|
|
prefix=self.form_prefix,
|
|
)
|
|
|
|
if self.form.is_valid():
|
|
result = self.get_result_data()
|
|
submitted_url = result["url"]
|
|
|
|
link_conversion = getattr(
|
|
settings,
|
|
"WAGTAILADMIN_EXTERNAL_LINK_CONVERSION",
|
|
LINK_CONVERSION_ALL,
|
|
).lower()
|
|
|
|
if link_conversion not in [
|
|
LINK_CONVERSION_ALL,
|
|
LINK_CONVERSION_EXACT,
|
|
LINK_CONVERSION_CONFIRM,
|
|
]:
|
|
# We should not attempt to convert external urls to page links
|
|
return self.render_chosen_response(result)
|
|
|
|
# Next, we should check if the url matches an internal page
|
|
# Strip the url of its query/fragment link parameters - these won't match a page
|
|
url_without_query = re.split(r"\?|#", submitted_url)[0]
|
|
|
|
# Start by finding any sites the url could potentially match
|
|
sites = getattr(request, "_wagtail_cached_site_root_paths", None)
|
|
if sites is None:
|
|
sites = Site.get_site_root_paths()
|
|
|
|
match_relative_paths = submitted_url.startswith("/") and len(sites) == 1
|
|
# We should only match relative urls if there's only a single site
|
|
# Otherwise this could get very annoying accidentally matching coincidentally
|
|
# named pages on different sites
|
|
|
|
if match_relative_paths:
|
|
possible_sites = [
|
|
(pk, url_without_query) for pk, path, url, language_code in sites
|
|
]
|
|
else:
|
|
possible_sites = [
|
|
(pk, url_without_query[len(url) :])
|
|
for pk, path, url, language_code in sites
|
|
if submitted_url.startswith(url)
|
|
]
|
|
|
|
# Loop over possible sites to identify a page match
|
|
for pk, url in possible_sites:
|
|
try:
|
|
route = Site.objects.get(pk=pk).root_page.specific.route(
|
|
request,
|
|
[component for component in url.split("/") if component],
|
|
)
|
|
|
|
matched_page = route.page.specific
|
|
|
|
internal_data = {
|
|
"id": matched_page.pk,
|
|
"parentId": matched_page.get_parent().pk,
|
|
"adminTitle": matched_page.draft_title,
|
|
"editUrl": reverse(
|
|
"wagtailadmin_pages:edit", args=(matched_page.pk,)
|
|
),
|
|
"url": matched_page.url,
|
|
}
|
|
|
|
# Let's check what this page's normal url would be
|
|
normal_url = (
|
|
matched_page.get_url_parts(request=request)[-1]
|
|
if match_relative_paths
|
|
else matched_page.get_full_url(request=request)
|
|
)
|
|
|
|
# If that's what the user provided, great. Let's just convert the external
|
|
# url to an internal link automatically unless we're set up tp manually check
|
|
# all conversions
|
|
if (
|
|
normal_url == submitted_url
|
|
and link_conversion != LINK_CONVERSION_CONFIRM
|
|
):
|
|
return self.render_chosen_response(internal_data)
|
|
# If not, they might lose query parameters or routable page information
|
|
|
|
if link_conversion == LINK_CONVERSION_EXACT:
|
|
# We should only convert exact matches
|
|
continue
|
|
|
|
# Let's confirm the conversion with them explicitly
|
|
else:
|
|
return render_modal_workflow(
|
|
request,
|
|
"wagtailadmin/chooser/confirm_external_to_internal.html",
|
|
None,
|
|
{
|
|
"submitted_url": submitted_url,
|
|
"internal_url": normal_url,
|
|
"page": matched_page.draft_title,
|
|
},
|
|
json_data={
|
|
"step": "confirm_external_to_internal",
|
|
"external": result,
|
|
"internal": internal_data,
|
|
},
|
|
)
|
|
|
|
except Http404:
|
|
continue
|
|
|
|
# Otherwise, with no internal matches, fall back to an external url
|
|
return self.render_chosen_response(result)
|
|
else: # form invalid
|
|
return self.render_form_response()
|
|
|
|
|
|
class AnchorLinkView(BaseLinkFormView):
|
|
form_prefix = "anchor-link-chooser"
|
|
form_class = AnchorLinkChooserForm
|
|
template_name = "wagtailadmin/chooser/anchor_link.html"
|
|
step_name = "anchor_link"
|
|
link_url_field_name = "url"
|
|
|
|
def get_url_from_field_value(self, value):
|
|
return "#" + value
|
|
|
|
|
|
class EmailLinkView(BaseLinkFormView):
|
|
form_prefix = "email-link-chooser"
|
|
form_class = EmailLinkChooserForm
|
|
template_name = "wagtailadmin/chooser/email_link.html"
|
|
step_name = "email_link"
|
|
link_url_field_name = "email_address"
|
|
|
|
def get_initial_data(self):
|
|
parsed_email = self.parse_email_link(self.request.GET.get("link_url", ""))
|
|
return {
|
|
"email_address": parsed_email["email"],
|
|
"link_text": self.request.GET.get("link_text", ""),
|
|
"subject": parsed_email["subject"],
|
|
"body": parsed_email["body"],
|
|
}
|
|
|
|
def get_url_from_field_value(self, value):
|
|
return "mailto:" + value
|
|
|
|
def get_result_data(self):
|
|
params = {
|
|
"subject": self.form.cleaned_data["subject"],
|
|
"body": self.form.cleaned_data["body"],
|
|
}
|
|
encoded_params = urlparse.urlencode(
|
|
{k: v for k, v in params.items() if v is not None and v != ""},
|
|
quote_via=urlparse.quote,
|
|
)
|
|
|
|
url = "mailto:" + self.form.cleaned_data["email_address"]
|
|
if encoded_params:
|
|
url += "?" + encoded_params
|
|
|
|
return {
|
|
"url": url,
|
|
"title": self.form.cleaned_data["link_text"].strip()
|
|
or self.form.cleaned_data["email_address"],
|
|
# If the user has explicitly entered / edited something in the link_text field,
|
|
# always use that text. If not, we should favour keeping the existing link/selection
|
|
# text, where applicable.
|
|
"prefer_this_title_as_link_text": ("link_text" in self.form.changed_data),
|
|
}
|
|
|
|
def post(self, request):
|
|
self.form = self.form_class(
|
|
request.POST, initial=self.get_initial_data(), prefix=self.form_prefix
|
|
)
|
|
|
|
if self.form.is_valid():
|
|
result = self.get_result_data()
|
|
return self.render_chosen_response(result)
|
|
else: # form invalid
|
|
return self.render_form_response()
|
|
|
|
def parse_email_link(self, mailto):
|
|
result = {}
|
|
|
|
mail_result = urlparse.urlparse(mailto)
|
|
|
|
result["email"] = mail_result.path
|
|
|
|
query = urlparse.parse_qs(mail_result.query)
|
|
result["subject"] = query["subject"][0] if "subject" in query else ""
|
|
result["body"] = query["body"][0] if "body" in query else ""
|
|
|
|
return result
|
|
|
|
|
|
class PhoneLinkView(BaseLinkFormView):
|
|
form_prefix = "phone-link-chooser"
|
|
form_class = PhoneLinkChooserForm
|
|
template_name = "wagtailadmin/chooser/phone_link.html"
|
|
step_name = "phone_link"
|
|
link_url_field_name = "phone_number"
|
|
|
|
def get_url_from_field_value(self, value):
|
|
value = re.sub(r"\s", "", value)
|
|
return "tel:" + value
|