Merge branch 'feature/chooser-refactor'

This commit is contained in:
Matt Westcott 2015-02-10 10:44:21 +00:00
commit 17ccb29371
27 changed files with 229 additions and 150 deletions

View file

@ -347,12 +347,6 @@
{% for field in example_form %}
{% if field.name == 'file' %}
{% include "wagtailimages/images/_file_field.html" %}
{% elif field.name == 'page_chooser' %}
<li>{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=field choose_one_text_str="Choose a page" choose_another_text_str="Choose another page" only %}</li>
{% elif field.name == 'image_chooser' %}
<li>{% include "wagtailimages/edit_handlers/image_chooser_panel.html" with field=field choose_one_text_str="Choose an image" choose_another_text_str="Choose another image" only %}</li>
{% elif field.name == 'document_chooser' %}
<li>{% include "wagtaildocs/edit_handlers/document_chooser_panel.html" with field=field choose_one_text_str="Choose a document" choose_another_text_str="Choose another document" only %}</li>
{% else %}
{% include "wagtailadmin/shared/field_as_li.html" %}
{% endif %}

View file

@ -5,16 +5,22 @@ from django.utils.safestring import mark_safe
class WidgetWithScript(Widget):
def render(self, name, value, attrs=None):
widget = super(WidgetWithScript, self).render(name, value, attrs)
def render_html(self, name, value, attrs):
"""Render the HTML (non-JS) portion of the field markup"""
return super(WidgetWithScript, self).render(name, value, attrs)
final_attrs = self.build_attrs(attrs, name=name)
id_ = final_attrs.get('id', None)
if id_ is None:
return widget
def render(self, name, value, attrs=None):
# no point trying to come up with sensible semantics for when 'id' is missing from attrs,
# so let's make sure it fails early in the process
try:
id_ = attrs['id']
except (KeyError, TypeError):
raise TypeError("WidgetWithScript cannot be rendered without an 'id' attribute")
widget_html = self.render_html(name, value, attrs)
js = self.render_js_init(id_, name, value)
out = '{0}<script>{1}</script>'.format(widget, js)
out = '{0}<script>{1}</script>'.format(widget_html, js)
return mark_safe(out)
def render_js_init(self, id_, name, value):

View file

@ -422,18 +422,16 @@ class BaseFieldPanel(EditHandler):
def render_as_object(self):
return mark_safe(render_to_string(self.object_template, {
'self': self,
'field_content': self.render_as_field(show_help_text=False),
'field_content': self.render_as_field(),
}))
field_template = "wagtailadmin/edit_handlers/field_panel_field.html"
def render_as_field(self, show_help_text=True, extra_context={}):
def render_as_field(self):
context = {
'field': self.bound_field,
'field_type': self.field_type(),
'show_help_text': show_help_text,
}
context.update(extra_context)
return mark_safe(render_to_string(self.field_template, context))
@classmethod
@ -482,7 +480,7 @@ class BaseChooserPanel(BaseFieldPanel):
a hidden foreign key input.
Subclasses provide:
* field_template
* field_template (only required if the default template of field_panel_field.html is not usable)
* object_type_name - something like 'image' which will be used as the var name
for the object instance in the field_template
"""
@ -496,20 +494,17 @@ class BaseChooserPanel(BaseFieldPanel):
# like every other unpopulated field type. Yay consistency!
return None
def render_as_field(self, show_help_text=True, extra_context={}):
def render_as_field(self):
instance_obj = self.get_chosen_item()
context = {
'field': self.bound_field,
self.object_type_name: instance_obj,
'is_chosen': bool(instance_obj),
'show_help_text': show_help_text,
'is_chosen': bool(instance_obj), # DEPRECATED - passed to templates for backwards compatibility only
}
context.update(extra_context)
return mark_safe(render_to_string(self.field_template, context))
class BasePageChooserPanel(BaseChooserPanel):
field_template = "wagtailadmin/edit_handlers/page_chooser_panel.html"
object_type_name = "page"
_target_content_type = None
@ -539,14 +534,6 @@ class BasePageChooserPanel(BaseChooserPanel):
return cls._target_content_type
def render_as_field(self, show_help_text=True, extra_context={}):
context = {
'choose_another_text_str': ugettext_lazy("Choose another page"),
'choose_one_text_str': ugettext_lazy("Choose a page"),
}
context.update(extra_context)
return super(BasePageChooserPanel, self).render_as_field(show_help_text, context)
class PageChooserPanel(object):
def __init__(self, field_name, page_type=None):

View file

@ -1,6 +1,12 @@
{% extends "wagtailadmin/shared/field.html" %}
{% load i18n %}
{% comment %}
------
DEPRECATED - provided for backwards compatibility with custom (third-party) chooser panels
created prior to Wagtail 0.9. New choosers should subclass wagtail.wagtailadmin.widgets.AdminChooser,
with a template that extends wagtailadmin/widgets/chooser.html.
------
Either the chosen or unchosen div will be shown, depending on the presence
of the 'blank' class on the container.
@ -10,21 +16,21 @@
{% block form_field %}
<div id="{{ field.id_for_label }}-chooser" class="chooser {% block chooser_class %}page-chooser{% endblock %} {% if not is_chosen %}blank{% endif %}">
<div id="{{ field.id_for_label }}-chooser" class="chooser {% block chooser_class %}page-chooser{% endblock %} {% if not field.value %}blank{% endif %}">
<div class="chosen">
{% block chosen_state_view %}{% endblock %}
<div class="actions">
{% if not field.field.required %}
<input type="button" class="action-clear button-small button-secondary" value="{% block clear_button_label %}{% trans "Clear choice" %}{% endblock %}">
<input type="button" class="action-clear button-small button-secondary" value="{{ field.field.widget.clear_choice_text }}">
{% endif %}
<input type="button" class="action-choose button-small button-secondary" value="{% block choose_another_button_label %}{% trans "Choose another item" %}{% endblock %}">
<input type="button" class="action-choose button-small button-secondary" value="{{ field.field.widget.choose_another_text }}">
</div>
</div>
<div class="unchosen">
<input type="button" class="action-choose button-small button-secondary" value="{% block choose_button_label %}{% trans "Choose an item" %}{% endblock %}">
<input type="button" class="action-choose button-small button-secondary" value="{{ field.field.widget.choose_one_text }}">
</div>
</div>

View file

@ -1,8 +1,2 @@
{% extends "wagtailadmin/edit_handlers/chooser_panel.html" %}
{% block chosen_state_view %}
<span class="title">{{ page.title }}</span>
{% endblock %}
{% block choose_another_button_label %}{{ choose_another_text_str }}{% endblock %}
{% block choose_button_label %}{{ choose_one_text_str }}{% endblock %}
{# Page chooser is now implemented as an entirely standard form widget - page_chooser_panel.html is redundant #}
{% include "wagtailadmin/shared/field.html" %}

View file

@ -13,12 +13,7 @@
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.new_title %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.new_slug %}
<li class="{% if form.new_parent_page.field.required %}required{% endif %}">
{% trans "Change page" as choose_another_text_str %}
{% trans "Choose page" as choose_one_text_str %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.new_parent_page page=parent_page is_chosen=True choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
</li>
{% include "wagtailadmin/shared/field_as_li.html" with field=form.new_parent_page %}
{% if form.copy_subpages %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.copy_subpages %}

View file

@ -0,0 +1,29 @@
{% load i18n %}
{% comment %}
Either the chosen or unchosen div will be shown, depending on the presence
of the 'blank' class on the container.
Any element with the 'action-choose' class will open the page chooser modal
when clicked.
{% endcomment %}
<div id="{{ attrs.id }}-chooser" class="chooser {% block chooser_class %}page-chooser{% endblock %} {% if not value %}blank{% endif %}">
<div class="chosen">
{% block chosen_state_view %}{% endblock %}
<div class="actions">
{% if not widget.is_required %}
<input type="button" class="action-clear button-small button-secondary" value="{{ widget.clear_choice_text }}">
{% endif %}
<input type="button" class="action-choose button-small button-secondary" value="{{ widget.choose_another_text }}">
</div>
</div>
<div class="unchosen">
<input type="button" class="action-choose button-small button-secondary" value="{{ widget.choose_one_text }}">
</div>
</div>
{{ original_field_html }}

View file

@ -0,0 +1,5 @@
{% extends "wagtailadmin/widgets/chooser.html" %}
{% block chosen_state_view %}
<span class="title">{{ page.title }}</span>
{% endblock %}

View file

@ -698,7 +698,6 @@ def copy(request, page_id):
return render(request, 'wagtailadmin/pages/copy.html', {
'page': page,
'parent_page': parent_page,
'form': form,
})

View file

@ -5,6 +5,8 @@ import json
from django.core.urlresolvers import reverse
from django.forms import widgets
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string
from wagtail.utils.widgets import WidgetWithScript
from wagtail.wagtailcore.models import Page
@ -34,14 +36,64 @@ class AdminTagWidget(WidgetWithScript, TagWidget):
json.dumps(reverse('wagtailadmin_tag_autocomplete')))
class AdminPageChooser(WidgetWithScript, widgets.Input):
class AdminChooser(WidgetWithScript, widgets.Input):
input_type = 'hidden'
choose_one_text = _("Choose an item")
choose_another_text = _("Choose another item")
clear_choice_text = _("Clear choice")
def get_instance(self, model_class, value):
# helper method for cleanly turning 'value' into an instance object
if value is None:
return None
try:
return model_class.objects.get(pk=value)
except model_class.DoesNotExist:
return None
def value_from_datadict(self, data, files, name):
# treat the empty string as None
result = super(AdminChooser, self).value_from_datadict(data, files, name)
if result == '':
return None
else:
return result
def __init__(self, **kwargs):
# allow choose_one_text / choose_another_text to be overridden per-instance
if 'choose_one_text' in kwargs:
self.choose_one_text = kwargs.pop('choose_one_text')
if 'choose_another_text' in kwargs:
self.choose_another_text = kwargs.pop('choose_another_text')
if 'clear_choice_text' in kwargs:
self.clear_choice_text = kwargs.pop('clear_choice_text')
super(AdminChooser, self).__init__(**kwargs)
class AdminPageChooser(AdminChooser):
target_content_type = None
choose_one_text = _('Choose a page')
choose_another_text = _('Choose another page')
def __init__(self, content_type=None, **kwargs):
super(AdminPageChooser, self).__init__(**kwargs)
self.target_content_type = content_type or ContentType.objects.get_for_model(Page)
def render_html(self, name, value, attrs):
original_field_html = super(AdminPageChooser, self).render_html(name, value, attrs)
model_class = self.target_content_type.model_class()
instance = self.get_instance(model_class, value)
return render_to_string("wagtailadmin/widgets/page_chooser.html", {
'widget': self,
'original_field_html': original_field_html,
'attrs': attrs,
'value': value,
'page': instance,
})
def render_js_init(self, id_, name, value):
page = Page.objects.get(pk=value) if value else None
parent = page.get_parent() if page else None

View file

@ -5,7 +5,6 @@ from .widgets import AdminDocumentChooser
class BaseDocumentChooserPanel(BaseChooserPanel):
field_template = "wagtaildocs/edit_handlers/document_chooser_panel.html"
object_type_name = "document"
@classmethod

View file

@ -1,11 +1,2 @@
{% extends "wagtailadmin/edit_handlers/chooser_panel.html" %}
{% load i18n %}
{% block chooser_class %}document-chooser{% endblock %}
{% block chosen_state_view %}
<span class="title">{{ document.title }}</span>
{% endblock %}
{% block clear_button_label %}{% trans "Clear choice" %}{% endblock %}
{% block choose_another_button_label %}{% trans "Choose another document" %}{% endblock %}
{% block choose_button_label %}{% trans "Choose a document" %}{% endblock %}
{# Document chooser is now implemented as an entirely standard form widget - document_chooser_panel.html is redundant #}
{% include "wagtailadmin/shared/field.html" %}

View file

@ -0,0 +1,6 @@
{% extends "wagtailadmin/widgets/chooser.html" %}
{% block chooser_class %}document-chooser{% endblock %}
{% block chosen_state_view %}
<span class="title">{{ document.title }}</span>
{% endblock %}

View file

@ -2,13 +2,29 @@ from __future__ import absolute_import, unicode_literals
import json
from django.forms import widgets
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from wagtail.utils.widgets import WidgetWithScript
from wagtail.wagtailadmin.widgets import AdminChooser
from wagtail.wagtaildocs.models import Document
class AdminDocumentChooser(WidgetWithScript, widgets.Input):
input_type = 'hidden'
class AdminDocumentChooser(AdminChooser):
choose_one_text = _('Choose a document')
choose_another_text = _('Choose another document')
def render_html(self, name, value, attrs):
original_field_html = super(AdminDocumentChooser, self).render_html(name, value, attrs)
instance = self.get_instance(Document, value)
return render_to_string("wagtaildocs/widgets/document_chooser.html", {
'widget': self,
'original_field_html': original_field_html,
'attrs': attrs,
'value': value,
'document': instance,
})
def render_js_init(self, id_, name, value):
return "createDocumentChooser({0});".format(json.dumps(id_))

View file

@ -5,7 +5,6 @@ from .widgets import AdminImageChooser
class BaseImageChooserPanel(BaseChooserPanel):
field_template = "wagtailimages/edit_handlers/image_chooser_panel.html"
object_type_name = "image"
@classmethod

View file

@ -1,19 +1,2 @@
{% extends "wagtailadmin/edit_handlers/chooser_panel.html" %}
{% load wagtailimages_tags %}
{% load i18n %}
{% block chooser_class %}image-chooser{% endblock %}
{% block chosen_state_view %}
<div class="preview-image">
{% if image %}
{% image image max-130x130 %}
{% else %}
<img>
{% endif %}
</div>
{% endblock %}
{% block clear_button_label %}{% trans "Clear image" %}{% endblock %}
{% block choose_another_button_label %}{% trans "Choose another image" %}{% endblock %}
{% block choose_button_label %}{% trans "Choose an image" %}{% endblock %}
{# Image chooser is now implemented as an entirely standard form widget - image_chooser_panel.html is redundant #}
{% include "wagtailadmin/shared/field.html" %}

View file

@ -0,0 +1,14 @@
{% extends "wagtailadmin/widgets/chooser.html" %}
{% load wagtailimages_tags %}
{% block chooser_class %}image-chooser{% endblock %}
{% block chosen_state_view %}
<div class="preview-image">
{% if image %}
{% image image max-130x130 %}
{% else %}
<img>
{% endif %}
</div>
{% endblock %}

View file

@ -2,13 +2,34 @@ from __future__ import absolute_import, unicode_literals
import json
from django.forms import widgets
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from wagtail.utils.widgets import WidgetWithScript
from wagtail.wagtailadmin.widgets import AdminChooser
from wagtail.wagtailimages.models import get_image_model
class AdminImageChooser(WidgetWithScript, widgets.Input):
input_type = 'hidden'
class AdminImageChooser(AdminChooser):
choose_one_text = _('Choose an image')
choose_another_text = _('Choose another image')
clear_choice_text = _('Clear image')
def __init__(self, **kwargs):
super(AdminImageChooser, self).__init__(**kwargs)
self.image_model = get_image_model()
def render_html(self, name, value, attrs):
original_field_html = super(AdminImageChooser, self).render_html(name, value, attrs)
instance = self.get_instance(self.image_model, value)
return render_to_string("wagtailimages/widgets/image_chooser.html", {
'widget': self,
'original_field_html': original_field_html,
'attrs': attrs,
'value': value,
'image': instance,
})
def render_js_init(self, id_, name, value):
return "createImageChooser({0});".format(json.dumps(id_))

View file

@ -10,13 +10,7 @@
<legend>{% trans "Promoted search result" %}</legend>
<ul class="fields">
<li class="model_choice_field">
{% trans "Choose another page" as choose_another_text_str %}
{% trans "Choose a page" as choose_one_text_str %}
{% if form.instance.page %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.page page=form.instance.page is_chosen=True choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% else %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.page is_chosen=False choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% endif %}
{% include "wagtailadmin/shared/field.html" with field=form.page only %}
</li>
<li class="char_field">
{% include "wagtailadmin/shared/field.html" with field=form.description only %}

View file

@ -1,4 +1,5 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore.models import Site
from wagtail.wagtailadmin.widgets import AdminPageChooser
@ -7,7 +8,9 @@ from wagtail.wagtailadmin.widgets import AdminPageChooser
class SiteForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SiteForm, self).__init__(*args, **kwargs)
self.fields['root_page'].widget = AdminPageChooser()
self.fields['root_page'].widget = AdminPageChooser(
choose_one_text=_('Choose a root page'), choose_another_text=_('Choose a different root page')
)
required_css_class = "required"

View file

@ -15,17 +15,7 @@
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.hostname %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.port %}
<li>
{% trans "Choose a different root page" as choose_another_text_str %}
{% trans "Choose a root page" as choose_one_text_str %}
{% if form.instance.root_page %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.root_page page=form.instance.root_page is_chosen=True choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% else %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.root_page is_chosen=False choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% endif %}
</li>
{% include "wagtailadmin/shared/field_as_li.html" with field=form.root_page %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.is_default_site %}
<li><input type="submit" value="{% trans 'Save' %}" /></li>

View file

@ -16,18 +16,7 @@
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.hostname %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.port %}
<li>
{% trans "Change page" as choose_another_text_str %}
{% trans "Choose page" as choose_one_text_str %}
{% if form.instance.root_page %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.root_page page=form.instance.root_page is_chosen=True choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% else %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.root_page is_chosen=False choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% endif %}
</li>
{% include "wagtailadmin/shared/field_as_li.html" with field=form.root_page %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.is_default_site %}
<li>

View file

@ -10,7 +10,6 @@ from .widgets import AdminSnippetChooser
class BaseSnippetChooserPanel(BaseChooserPanel):
field_template = "wagtailsnippets/edit_handlers/snippet_chooser_panel.html"
object_type_name = 'item'
_content_type = None
@ -18,7 +17,7 @@ class BaseSnippetChooserPanel(BaseChooserPanel):
@classmethod
def widget_overrides(cls):
return {cls.field_name: AdminSnippetChooser(
content_type=cls.content_type())}
content_type=cls.content_type(), snippet_type_name=cls.snippet_type_name)}
@classmethod
def content_type(cls):
@ -28,14 +27,12 @@ class BaseSnippetChooserPanel(BaseChooserPanel):
return cls._content_type
def render_as_field(self, show_help_text=True):
def render_as_field(self):
instance_obj = self.get_chosen_item()
return mark_safe(render_to_string(self.field_template, {
'field': self.bound_field,
self.object_type_name: instance_obj,
'snippet_type_name': self.snippet_type_name,
'is_chosen': bool(instance_obj),
'show_help_text': show_help_text,
}))

View file

@ -1,11 +1,2 @@
{% extends "wagtailadmin/edit_handlers/chooser_panel.html" %}
{% load i18n %}
{% block chooser_class %}snippet-chooser{% endblock %}
{% block chosen_state_view %}
<span class="title">{% if is_chosen %}{{ item }}{% endif %}</span>
{% endblock %}
{% block choose_another_button_label %}{% blocktrans %}Choose another {{ snippet_type_name }}{% endblocktrans %}{% endblock %}
{% block choose_button_label %}{% blocktrans %}Choose {{ snippet_type_name }}{% endblocktrans %}{% endblock %}
{# Snippet chooser is now implemented as an entirely standard form widget - snippet_chooser_panel.html is redundant #}
{% include "wagtailadmin/shared/field.html" %}

View file

@ -0,0 +1,7 @@
{% extends "wagtailadmin/widgets/chooser.html" %}
{% block chooser_class %}snippet-chooser{% endblock %}
{% block chosen_state_view %}
<span class="title">{{ item }}</span>
{% endblock %}

View file

@ -2,20 +2,39 @@ from __future__ import absolute_import, unicode_literals
import json
from django.forms import widgets
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from wagtail.utils.widgets import WidgetWithScript
from wagtail.wagtailadmin.widgets import AdminChooser
class AdminSnippetChooser(WidgetWithScript, widgets.Input):
input_type = 'hidden'
class AdminSnippetChooser(AdminChooser):
target_content_type = None
def __init__(self, content_type=None, **kwargs):
if 'snippet_type_name' in kwargs:
snippet_type_name = kwargs.pop('snippet_type_name')
self.choose_one_text = _('Choose %s') % snippet_type_name
self.choose_another_text = _('Choose another %s') % snippet_type_name
super(AdminSnippetChooser, self).__init__(**kwargs)
if content_type is not None:
self.target_content_type = content_type
def render_html(self, name, value, attrs):
original_field_html = super(AdminSnippetChooser, self).render_html(name, value, attrs)
model_class = self.target_content_type.model_class()
instance = self.get_instance(model_class, value)
return render_to_string("wagtailsnippets/widgets/snippet_chooser.html", {
'widget': self,
'original_field_html': original_field_html,
'attrs': attrs,
'value': value,
'item': instance,
})
def render_js_init(self, id_, name, value):
content_type = self.target_content_type

View file

@ -1,14 +1,7 @@
{% load i18n %}
<td>
{% trans "Edit page" as choose_another_text_str %}
{% trans "Choose page" as choose_one_text_str %}
{% if form.instance.page %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.page page=form.instance.page is_chosen=True choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% else %}
{% include "wagtailadmin/edit_handlers/page_chooser_panel.html" with field=form.page is_chosen=False choose_one_text_str=choose_one_text_str choose_another_text_str=choose_another_text_str only %}
{% endif %}
{% include "wagtailadmin/edit_handlers/field_panel_field.html" with field=form.page only %}
</td>
<td>
{% include "wagtailadmin/edit_handlers/field_panel_field.html" with field=form.permission_type only %}