mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-17 19:51:11 +00:00
Using ContentTypes can lead to the awkward case where they are used before the database is migrated, leading to errors on start up. Removing ContentTypes and using plain Model classes instead makes this impossible, and also makes the code clearer. Fixes #2433
198 lines
7.3 KiB
Python
198 lines
7.3 KiB
Python
from __future__ import absolute_import, unicode_literals
|
|
|
|
import json
|
|
|
|
from django.utils.formats import get_format
|
|
from django.core.urlresolvers import reverse
|
|
from django.forms import widgets
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.utils.functional import cached_property
|
|
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
|
|
|
|
from taggit.forms import TagWidget
|
|
|
|
|
|
class AdminAutoHeightTextInput(WidgetWithScript, widgets.Textarea):
|
|
def __init__(self, attrs=None):
|
|
# Use more appropriate rows default, given autoheight will alter this anyway
|
|
default_attrs = {'rows': '1'}
|
|
if attrs:
|
|
default_attrs.update(attrs)
|
|
|
|
super(AdminAutoHeightTextInput, self).__init__(default_attrs)
|
|
|
|
def render_js_init(self, id_, name, value):
|
|
return 'autosize($("#{0}"));'.format(id_)
|
|
|
|
|
|
class AdminDateInput(WidgetWithScript, widgets.DateInput):
|
|
# Set a default date format to match the one that our JS date picker expects -
|
|
# it can still be overridden explicitly, but this way it won't be affected by
|
|
# the DATE_INPUT_FORMATS setting
|
|
def __init__(self, attrs=None, format='%Y-%m-%d'):
|
|
super(AdminDateInput, self).__init__(attrs=attrs, format=format)
|
|
|
|
def render_js_init(self, id_, name, value):
|
|
return 'initDateChooser({0}, {1});'.format(
|
|
json.dumps(id_),
|
|
json.dumps({'dayOfWeekStart': get_format('FIRST_DAY_OF_WEEK')})
|
|
)
|
|
|
|
|
|
class AdminTimeInput(WidgetWithScript, widgets.TimeInput):
|
|
def __init__(self, attrs=None, format='%H:%M'):
|
|
super(AdminTimeInput, self).__init__(attrs=attrs, format=format)
|
|
|
|
def render_js_init(self, id_, name, value):
|
|
return 'initTimeChooser({0});'.format(json.dumps(id_))
|
|
|
|
|
|
class AdminDateTimeInput(WidgetWithScript, widgets.DateTimeInput):
|
|
def __init__(self, attrs=None, format='%Y-%m-%d %H:%M'):
|
|
super(AdminDateTimeInput, self).__init__(attrs=attrs, format=format)
|
|
|
|
def render_js_init(self, id_, name, value):
|
|
return 'initDateTimeChooser({0}, {1});'.format(
|
|
json.dumps(id_),
|
|
json.dumps({'dayOfWeekStart': get_format('FIRST_DAY_OF_WEEK')})
|
|
)
|
|
|
|
|
|
class AdminTagWidget(WidgetWithScript, TagWidget):
|
|
def render_js_init(self, id_, name, value):
|
|
return "initTagField({0}, {1});".format(
|
|
json.dumps(id_),
|
|
json.dumps(reverse('wagtailadmin_tag_autocomplete')))
|
|
|
|
|
|
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")
|
|
link_to_chosen_text = _("Edit this item")
|
|
show_edit_link = True
|
|
|
|
# when looping over form fields, this one should appear in visible_fields, not hidden_fields
|
|
# despite the underlying input being type="hidden"
|
|
is_hidden = False
|
|
|
|
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 get_instance_and_id(self, model_class, value):
|
|
if value is None:
|
|
return (None, None)
|
|
elif isinstance(value, model_class):
|
|
return (value, value.pk)
|
|
else:
|
|
try:
|
|
return (model_class.objects.get(pk=value), value)
|
|
except model_class.DoesNotExist:
|
|
return (None, 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')
|
|
if 'link_to_chosen_text' in kwargs:
|
|
self.link_to_chosen_text = kwargs.pop('link_to_chosen_text')
|
|
if 'show_edit_link' in kwargs:
|
|
self.show_edit_link = kwargs.pop('show_edit_link')
|
|
super(AdminChooser, self).__init__(**kwargs)
|
|
|
|
|
|
class AdminPageChooser(AdminChooser):
|
|
choose_one_text = _('Choose a page')
|
|
choose_another_text = _('Choose another page')
|
|
link_to_chosen_text = _('Edit this page')
|
|
|
|
def __init__(self, target_models=None, content_type=None, can_choose_root=False, **kwargs):
|
|
super(AdminPageChooser, self).__init__(**kwargs)
|
|
|
|
self.target_models = list(target_models or [Page])
|
|
|
|
if content_type is not None:
|
|
if target_models is not None:
|
|
raise ValueError("Can not set both target_models and content_type")
|
|
if isinstance(content_type, ContentType):
|
|
self.target_models = [content_type.model_class()]
|
|
else:
|
|
self.target_models = [ct.model_class() for ct in content_type]
|
|
|
|
self.can_choose_root = can_choose_root
|
|
|
|
@cached_property
|
|
def target_content_types(self):
|
|
return list(ContentType.objects.get_for_models(*self.target_models).values())
|
|
|
|
def _get_lowest_common_page_class(self):
|
|
"""
|
|
Return a Page class that is an ancestor for all Page classes in
|
|
``target_models``, and is also a concrete Page class itself.
|
|
"""
|
|
if len(self.target_models) == 1:
|
|
# Shortcut for a single page type
|
|
return self.target_models[0]
|
|
else:
|
|
return Page
|
|
|
|
def render_html(self, name, value, attrs):
|
|
model_class = self._get_lowest_common_page_class()
|
|
|
|
instance, value = self.get_instance_and_id(model_class, value)
|
|
|
|
original_field_html = super(AdminPageChooser, self).render_html(name, value, attrs)
|
|
|
|
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):
|
|
if isinstance(value, Page):
|
|
page = value
|
|
else:
|
|
# Value is an ID look up object
|
|
model_class = self._get_lowest_common_page_class()
|
|
page = self.get_instance(model_class, value)
|
|
|
|
parent = page.get_parent() if page else None
|
|
|
|
return "createPageChooser({id}, {model_names}, {parent}, {can_choose_root});".format(
|
|
id=json.dumps(id_),
|
|
model_names=json.dumps([
|
|
'{app}.{model}'.format(
|
|
app=model._meta.app_label,
|
|
model=model._meta.model_name)
|
|
for model in self.target_models
|
|
]),
|
|
parent=json.dumps(parent.id if parent else None),
|
|
can_choose_root=('true' if self.can_choose_root else 'false')
|
|
)
|