mirror of
https://github.com/wagtail/wagtail.git
synced 2024-12-01 11:41:20 +01:00
Revert "Multiple page types in page chooser"
This commit is contained in:
parent
31c6198afb
commit
85707d5925
@ -163,15 +163,10 @@ PageChooserPanel
|
||||
)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
PageChooserPanel('related_page', ['demo.PublisherPage', 'demo.EventPage']),
|
||||
PageChooserPanel('related_page', 'demo.PublisherPage'),
|
||||
]
|
||||
|
||||
``PageChooserPanel`` takes two arguments: a field name and an optional list of page types.
|
||||
|
||||
The ``page_types`` parameter restricts which page types can be selected in this field. It defaults to just the model that the ``ForeignKey`` is pointing at. A ``ForeignKey`` pointing at ``wagtailcore.Page``, such as the one in the example above, will allow all pages to be selected, as all pages must subclass ``wagtailcore.Page``.
|
||||
|
||||
This parameter will take a Page subclass, a string (in the form of an ``"app_name.ModelName"`` string) or a list of strings/model classes.
|
||||
|
||||
``PageChooserPanel`` takes two arguments: a field name and an optional page type. Specifying a page type (in the form of an ``"appname.modelname"`` string) will filter the chooser to display only pages of that type.
|
||||
|
||||
ImageChooserPanel
|
||||
-----------------
|
||||
|
@ -544,66 +544,44 @@ class BaseChooserPanel(BaseFieldPanel):
|
||||
class BasePageChooserPanel(BaseChooserPanel):
|
||||
object_type_name = "page"
|
||||
|
||||
_target_content_types = None
|
||||
_target_content_type = None
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
return {cls.field_name: widgets.AdminPageChooser(
|
||||
content_types=cls.target_content_types())}
|
||||
content_type=cls.target_content_type())}
|
||||
|
||||
@classmethod
|
||||
def target_content_types(cls):
|
||||
if cls._target_content_types is None:
|
||||
if cls.page_types:
|
||||
models = []
|
||||
def target_content_type(cls):
|
||||
if cls._target_content_type is None:
|
||||
if cls.page_type:
|
||||
try:
|
||||
model = resolve_model_string(cls.page_type)
|
||||
except LookupError:
|
||||
raise ImproperlyConfigured("{0}.page_type must be of the form 'app_label.model_name', given {1!r}".format(
|
||||
cls.__name__, cls.page_type))
|
||||
except ValueError:
|
||||
raise ImproperlyConfigured("{0}.page_type refers to model {1!r} that has not been installed".format(
|
||||
cls.__name__, cls.page_type))
|
||||
|
||||
for index, page_type in enumerate(cls.page_types):
|
||||
try:
|
||||
models.append(resolve_model_string(page_type))
|
||||
except LookupError:
|
||||
raise ImproperlyConfigured("{0}.page_types[{1}] must be of the form 'app_label.model_name', given {2!r}".format(
|
||||
cls.__name__, index, page_type))
|
||||
except ValueError:
|
||||
raise ImproperlyConfigured("{0}.page_types[{1}] refers to model {2!r} that has not been installed".format(
|
||||
cls.__name__, index, page_type))
|
||||
|
||||
# Get a mapping of ContentType objects for the models
|
||||
content_types = ContentType.objects.get_for_models(*models)
|
||||
|
||||
# Convert content types into a list and set it to _target_content_types
|
||||
# As the content types were in a dict, the order would be randomised but
|
||||
# we need the ordering on _target_content_types to be deterministic to
|
||||
# simplify testing
|
||||
cls._target_content_types = [
|
||||
content_types[model]
|
||||
for model in models
|
||||
]
|
||||
cls._target_content_type = ContentType.objects.get_for_model(model)
|
||||
else:
|
||||
# Find model by introspecting the ForeignKey
|
||||
model = cls.model._meta.get_field(cls.field_name).rel.to
|
||||
cls._target_content_types = [ContentType.objects.get_for_model(model)]
|
||||
target_model = cls.model._meta.get_field(cls.field_name).rel.to
|
||||
cls._target_content_type = ContentType.objects.get_for_model(target_model)
|
||||
|
||||
return cls._target_content_types
|
||||
return cls._target_content_type
|
||||
|
||||
|
||||
class PageChooserPanel(object):
|
||||
def __init__(self, field_name, page_types=None):
|
||||
def __init__(self, field_name, page_type=None):
|
||||
self.field_name = field_name
|
||||
|
||||
# Make sure page_types is a list
|
||||
if page_types is None:
|
||||
page_types = []
|
||||
|
||||
if not isinstance(page_types, (list, tuple)):
|
||||
page_types = [page_types]
|
||||
|
||||
self.page_types = page_types
|
||||
self.page_type = page_type
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_PageChooserPanel'), (BasePageChooserPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
'page_types': self.page_types,
|
||||
'page_type': self.page_type,
|
||||
})
|
||||
|
||||
|
||||
|
@ -1,21 +1,20 @@
|
||||
{% load i18n %}
|
||||
{% if page_types_restricted %}
|
||||
{% trans "Choose" as choose_str %}
|
||||
{% trans page_type_name as subtitle %}
|
||||
{% else %}
|
||||
{% trans "Choose a page" as choose_str %}
|
||||
{% endif %}
|
||||
|
||||
{% include "wagtailadmin/shared/header.html" with title=choose_str subtitle=page_type_names|join:", " search_url="wagtailadmin_choose_page_search" query_parameters="page_types="|add:page_type_string icon="doc-empty-inverse" %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=choose_str subtitle=subtitle search_url="wagtailadmin_choose_page_search" query_parameters="page_type="|add:page_type_string icon="doc-empty-inverse" %}
|
||||
|
||||
<div class="nice-padding">
|
||||
{% include 'wagtailadmin/chooser/_link_types.html' with current='internal' %}
|
||||
|
||||
{% if page_types_restricted %}
|
||||
<p class="help-block help-warning">
|
||||
{% blocktrans with type=page_type_names|join:", " count counter=page_type_names|length %}
|
||||
{% blocktrans with type=page_type_name %}
|
||||
Only pages of type "{{ type }}" may be chosen for this field. Search results will exclude pages of other types.
|
||||
{% plural %}
|
||||
Only the following page types may be chosen for this field: {{ type }}. Search results will exclude pages of other types.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
@ -367,7 +367,7 @@ class TestPageChooserPanel(TestCase):
|
||||
|
||||
def test_render_js_init(self):
|
||||
result = self.page_chooser_panel.render_as_field()
|
||||
expected_js = 'createPageChooser("{id}", ["{model}"], {parent});'.format(
|
||||
expected_js = 'createPageChooser("{id}", "{model}", {parent});'.format(
|
||||
id="id_page", model="wagtailcore.page", parent=self.events_index_page.id)
|
||||
|
||||
self.assertIn(expected_js, result)
|
||||
@ -400,25 +400,11 @@ class TestPageChooserPanel(TestCase):
|
||||
page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
|
||||
result = page_chooser_panel.render_as_field()
|
||||
expected_js = 'createPageChooser("{id}", ["{model}"], {parent});'.format(
|
||||
expected_js = 'createPageChooser("{id}", "{model}", {parent});'.format(
|
||||
id="id_page", model="tests.eventpage", parent=self.events_index_page.id)
|
||||
|
||||
self.assertIn(expected_js, result)
|
||||
|
||||
def test_multiple_page_types(self):
|
||||
# Model has a foreign key to Page, but we specify EventPage, and SimplePage in
|
||||
# the PageChooserPanel to restrict the chooser to that page type
|
||||
MyPageChooserPanel = PageChooserPanel('page', ['tests.EventPage', 'tests.SimplePage']).bind_to_model(EventPageChooserModel)
|
||||
PageChooserForm = MyPageChooserPanel.get_form_class(EventPageChooserModel)
|
||||
form = PageChooserForm(instance=self.test_instance)
|
||||
page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
|
||||
result = page_chooser_panel.render_as_field()
|
||||
expected_js = 'createPageChooser("{id}", ["{model1}", "{model2}"], {parent});'.format(
|
||||
id="id_page", model1="tests.eventpage", model2="tests.simplepage", parent=self.events_index_page.id)
|
||||
|
||||
self.assertIn(expected_js, result)
|
||||
|
||||
def test_autodetect_page_type(self):
|
||||
# Model has a foreign key to EventPage, which we want to autodetect
|
||||
# instead of specifying the page type in PageChooserPanel
|
||||
@ -428,41 +414,33 @@ class TestPageChooserPanel(TestCase):
|
||||
page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
|
||||
result = page_chooser_panel.render_as_field()
|
||||
expected_js = 'createPageChooser("{id}", ["{model}"], {parent});'.format(
|
||||
expected_js = 'createPageChooser("{id}", "{model}", {parent});'.format(
|
||||
id="id_page", model="tests.eventpage", parent=self.events_index_page.id)
|
||||
|
||||
self.assertIn(expected_js, result)
|
||||
|
||||
def test_target_content_types(self):
|
||||
def test_target_content_type(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
'tests.simplepage'
|
||||
).bind_to_model(PageChooserModel).target_content_types()
|
||||
self.assertEqual(result[0].name, 'simple page')
|
||||
'wagtailcore.site'
|
||||
).bind_to_model(PageChooserModel).target_content_type()
|
||||
self.assertEqual(result.name, 'Site')
|
||||
|
||||
def test_target_content_types_multiple_page_types(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
['tests.simplepage', 'tests.eventpage'],
|
||||
).bind_to_model(PageChooserModel).target_content_types()
|
||||
self.assertEqual(result[0].name, 'simple page')
|
||||
self.assertEqual(result[1].name, 'event page')
|
||||
|
||||
def test_target_content_types_malformed_type(self):
|
||||
def test_target_content_type_malformed_type(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
'snowman'
|
||||
).bind_to_model(PageChooserModel)
|
||||
self.assertRaises(ImproperlyConfigured,
|
||||
result.target_content_types)
|
||||
result.target_content_type)
|
||||
|
||||
def test_target_content_types_nonexistent_type(self):
|
||||
def test_target_content_type_nonexistent_type(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
'snowman.lorry'
|
||||
).bind_to_model(PageChooserModel)
|
||||
self.assertRaises(ImproperlyConfigured,
|
||||
result.target_content_types)
|
||||
result.target_content_type)
|
||||
|
||||
|
||||
class TestInlinePanel(TestCase, WagtailTestUtils):
|
||||
|
@ -41,7 +41,7 @@ class TestChooserBrowse(TestCase, WagtailTestUtils):
|
||||
self.assertContains(response, "There are 0 matches")
|
||||
|
||||
def test_get_invalid(self):
|
||||
response = self.search({'page_types': 'foo.bar'})
|
||||
response = self.search({'page_type': 'foo.bar'})
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
|
@ -7,13 +7,12 @@ from django.utils.http import urlencode
|
||||
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
|
||||
from wagtail.wagtailadmin.forms import SearchForm, ExternalLinkChooserForm, ExternalLinkChooserWithLinkTextForm, EmailLinkChooserForm, EmailLinkChooserWithLinkTextForm
|
||||
|
||||
from wagtail.wagtailcore.utils import resolve_model_string
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
def get_querystring(request):
|
||||
return urlencode({
|
||||
'page_types': request.GET.get('page_types', ''),
|
||||
'page_type': request.GET.get('page_type', ''),
|
||||
'allow_external_link': request.GET.get('allow_external_link', ''),
|
||||
'allow_email_link': request.GET.get('allow_email_link', ''),
|
||||
'prompt_for_link_text': request.GET.get('prompt_for_link_text', ''),
|
||||
@ -33,27 +32,25 @@ def shared_context(request, extra_context={}):
|
||||
def browse(request, parent_page_id=None):
|
||||
ITEMS_PER_PAGE = 25
|
||||
|
||||
page_types = request.GET.get('page_types', 'wagtailcore.page').split(',')
|
||||
page_type = request.GET.get('page_type') or 'wagtailcore.page'
|
||||
content_type_app_name, content_type_model_name = page_type.split('.')
|
||||
|
||||
desired_classes = []
|
||||
for page_type in page_types:
|
||||
try:
|
||||
content_type = resolve_model_string(page_type)
|
||||
except LookupError:
|
||||
raise Http404
|
||||
|
||||
desired_classes.append(content_type)
|
||||
try:
|
||||
content_type = ContentType.objects.get_by_natural_key(content_type_app_name, content_type_model_name)
|
||||
except ContentType.DoesNotExist:
|
||||
raise Http404
|
||||
desired_class = content_type.model_class()
|
||||
|
||||
if parent_page_id:
|
||||
parent_page = get_object_or_404(Page, id=parent_page_id)
|
||||
else:
|
||||
parent_page = Page.get_first_root_node()
|
||||
|
||||
parent_page.can_choose = issubclass(parent_page.specific_class, tuple(desired_classes))
|
||||
parent_page.can_choose = issubclass(parent_page.specific_class, desired_class)
|
||||
search_form = SearchForm()
|
||||
pages = parent_page.get_children()
|
||||
|
||||
if desired_classes == [Page]:
|
||||
if desired_class == Page:
|
||||
# apply pagination first, since we know that the page listing won't
|
||||
# have to be filtered, and that saves us walking the entire list
|
||||
p = request.GET.get('p', 1)
|
||||
@ -76,7 +73,7 @@ def browse(request, parent_page_id=None):
|
||||
|
||||
shown_pages = []
|
||||
for page in pages:
|
||||
page.can_choose = issubclass(page.specific_class or Page, tuple(desired_classes))
|
||||
page.can_choose = issubclass(page.specific_class or Page, desired_class)
|
||||
page.can_descend = page.get_children_count()
|
||||
|
||||
if page.can_choose or page.can_descend:
|
||||
@ -99,42 +96,30 @@ def browse(request, parent_page_id=None):
|
||||
'parent_page': parent_page,
|
||||
'pages': pages,
|
||||
'search_form': search_form,
|
||||
'page_type_string': ','.join(page_types),
|
||||
'page_type_names': [desired_class.get_verbose_name() for desired_class in desired_classes],
|
||||
'page_type_string': page_type,
|
||||
'page_type_name': desired_class.get_verbose_name(),
|
||||
'page_types_restricted': (page_type != 'wagtailcore.page')
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
def search(request, parent_page_id=None):
|
||||
page_types = request.GET.get('page_types')
|
||||
content_types = []
|
||||
page_type = request.GET.get('page_type') or 'wagtailcore.page'
|
||||
content_type_app_name, content_type_model_name = page_type.split('.')
|
||||
|
||||
# Convert page_types string into list of ContentType objects
|
||||
if page_types:
|
||||
try:
|
||||
content_types = ContentType.objects.get_for_models(*[
|
||||
resolve_model_string(page_type) for page_type in page_types.split(',')])
|
||||
except LookupError:
|
||||
raise Http404
|
||||
try:
|
||||
content_type = ContentType.objects.get_by_natural_key(content_type_app_name, content_type_model_name)
|
||||
except ContentType.DoesNotExist:
|
||||
raise Http404
|
||||
desired_class = content_type.model_class()
|
||||
|
||||
search_form = SearchForm(request.GET)
|
||||
if search_form.is_valid() and search_form.cleaned_data['q']:
|
||||
pages = Page.objects.exclude(
|
||||
pages = desired_class.objects.exclude(
|
||||
depth=1 # never include root
|
||||
)
|
||||
|
||||
# Restrict content types
|
||||
if content_types:
|
||||
pages = pages.filter(content_type__in=content_types)
|
||||
|
||||
# Do search
|
||||
pages = pages.filter(title__icontains=search_form.cleaned_data['q'])
|
||||
|
||||
# Truncate results
|
||||
pages = pages[:10]
|
||||
).filter(title__icontains=search_form.cleaned_data['q'])[:10]
|
||||
else:
|
||||
pages = Page.objects.none()
|
||||
pages = desired_class.objects.none()
|
||||
|
||||
shown_pages = []
|
||||
for page in pages:
|
||||
|
@ -110,21 +110,17 @@ class AdminChooser(WidgetWithScript, widgets.Input):
|
||||
|
||||
|
||||
class AdminPageChooser(AdminChooser):
|
||||
target_content_types = None
|
||||
target_content_type = None
|
||||
choose_one_text = _('Choose a page')
|
||||
choose_another_text = _('Choose another page')
|
||||
link_to_chosen_text = _('Edit this page')
|
||||
|
||||
def __init__(self, content_types=None, **kwargs):
|
||||
def __init__(self, content_type=None, **kwargs):
|
||||
super(AdminPageChooser, self).__init__(**kwargs)
|
||||
self.target_content_types = content_types or [ContentType.objects.get_for_model(Page)]
|
||||
self.target_content_type = content_type or ContentType.objects.get_for_model(Page)
|
||||
|
||||
def render_html(self, name, value, attrs):
|
||||
if len(self.target_content_types) == 1:
|
||||
model_class = self.target_content_types[0].model_class()
|
||||
else:
|
||||
model_class = Page
|
||||
|
||||
model_class = self.target_content_type.model_class()
|
||||
instance, value = self.get_instance_and_id(model_class, value)
|
||||
|
||||
original_field_html = super(AdminPageChooser, self).render_html(name, value, attrs)
|
||||
@ -138,24 +134,17 @@ class AdminPageChooser(AdminChooser):
|
||||
})
|
||||
|
||||
def render_js_init(self, id_, name, value):
|
||||
if isinstance(value, Page):
|
||||
model_class = self.target_content_type.model_class()
|
||||
if isinstance(value, model_class):
|
||||
page = value
|
||||
else:
|
||||
if len(self.target_content_types) == 1:
|
||||
model_class = self.target_content_types[0].model_class()
|
||||
else:
|
||||
model_class = Page
|
||||
|
||||
page = self.get_instance(model_class, value)
|
||||
|
||||
parent = page.get_parent() if page else None
|
||||
content_type = self.target_content_type
|
||||
|
||||
return "createPageChooser({id}, {content_type}, {parent});".format(
|
||||
id=json.dumps(id_),
|
||||
content_type=json.dumps([
|
||||
'{app}.{model}'.format(
|
||||
app=content_type.app_label,
|
||||
model=content_type.model)
|
||||
for content_type in self.target_content_types
|
||||
]),
|
||||
content_type=json.dumps('{app}.{model}'.format(
|
||||
app=content_type.app_label,
|
||||
model=content_type.model)),
|
||||
parent=json.dumps(parent.id if parent else None))
|
||||
|
Loading…
Reference in New Issue
Block a user