Implement nested collections

pull/6383/head
Robert Rollins 2020-09-02 22:12:30 +01:00 zatwierdzone przez Andy Babic
rodzic 0d8301e28f
commit e404f83cd1
17 zmienionych plików z 238 dodań i 63 usunięć

Wyświetl plik

@ -3,6 +3,7 @@ from itertools import groupby
from django import forms from django import forms
from django.contrib.auth.models import Group, Permission from django.contrib.auth.models import Group, Permission
from django.db import transaction from django.db import transaction
from django.db.models import Min
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -18,11 +19,76 @@ class CollectionViewRestrictionForm(BaseViewRestrictionForm):
fields = ('restriction_type', 'password', 'groups') fields = ('restriction_type', 'password', 'groups')
class SelectWithDisabledOptions(forms.Select):
"""
Subclass of Django's select widget that allows disabling options.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.disabled_values = ()
def create_option(self, name, value, *args, **kwargs):
option_dict = super().create_option(name, value, *args, **kwargs)
if value in self.disabled_values:
option_dict['attrs']['disabled'] = 'disabled'
return option_dict
class CollectionChoiceField(forms.ModelChoiceField):
widget = SelectWithDisabledOptions
def __init__(self, *args, disabled_queryset=None, **kwargs):
super().__init__(*args, **kwargs)
self._indentation_start_depth = 2
self.disabled_queryset = disabled_queryset
def _get_disabled_queryset(self):
return self._disabled_queryset
def _set_disabled_queryset(self, queryset):
self._disabled_queryset = queryset
if queryset is None:
self.widget.disabled_values = ()
else:
self.widget.disabled_values = queryset.values_list(self.to_field_name or 'pk', flat=True)
disabled_queryset = property(_get_disabled_queryset, _set_disabled_queryset)
def _set_queryset(self, queryset):
min_depth = self.queryset.aggregate(Min('depth'))['depth__min']
if min_depth is None:
self._indentation_start_depth = 2
else:
self._indentation_start_depth = min_depth + 1
def label_from_instance(self, obj):
return obj.get_indented_name(self._indentation_start_depth, html=True)
class CollectionForm(forms.ModelForm): class CollectionForm(forms.ModelForm):
parent = CollectionChoiceField(
queryset=Collection.objects.all(),
required=False,
help_text=_(
"Select hierarchical position. Note: a collection cannot become a child of itself or one of its "
"descendants."
)
)
class Meta: class Meta:
model = Collection model = Collection
fields = ('name',) fields = ('name',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance._state.adding:
self.initial['parent'] = Collection.get_first_root_node().pk
else:
self.initial['parent'] = self.instance.get_parent().pk
self.fields['parent'].disabled_queryset = self.instance.get_descendants(inclusive=True)
class BaseCollectionMemberForm(forms.ModelForm): class BaseCollectionMemberForm(forms.ModelForm):
""" """
@ -212,8 +278,9 @@ def collection_member_permission_formset_factory(
defines the permissions that are assigned to an entity defines the permissions that are assigned to an entity
(i.e. group or user) for a specific collection (i.e. group or user) for a specific collection
""" """
collection = forms.ModelChoiceField( collection = CollectionChoiceField(
queryset=Collection.objects.all().prefetch_related('group_permissions') queryset=Collection.objects.all().prefetch_related('group_permissions'),
empty_label=None
) )
permissions = PermissionMultipleChoiceField( permissions = PermissionMultipleChoiceField(
queryset=permission_queryset, queryset=permission_queryset,

Wyświetl plik

@ -1,5 +1,5 @@
{% extends "wagtailadmin/generic/index.html" %} {% extends "wagtailadmin/generic/index.html" %}
{% load i18n %} {% load i18n wagtailadmin_tags %}
{% block listing %} {% block listing %}
<div class="nice-padding"> <div class="nice-padding">
@ -14,11 +14,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% minimum_collection_depth collections as min_depth %}
{% for collection in collections %} {% for collection in collections %}
<tr> <tr>
<td class="title"> <td class="title">
<div class="title-wrapper"> <div class="title-wrapper">
<a href="{% url 'wagtailadmin_collections:edit' collection.id %}">{{ collection }}</a> <a href="{% url 'wagtailadmin_collections:edit' collection.id %}">
{% format_collection collection min_depth %}
</a>
</div> </div>
</td> </td>
</tr> </tr>

Wyświetl plik

@ -1,5 +1,4 @@
{% load i18n %} {% load i18n l10n wagtailadmin_tags %}
{% load l10n %}
<li> <li>
<div class="field choice_field select"> <div class="field choice_field select">
@ -8,8 +7,18 @@
<div class="input"> <div class="input">
<select id="collection_chooser_collection_id" name="collection_id"> <select id="collection_chooser_collection_id" name="collection_id">
<option value="">{% trans "All collections" %}</option> <option value="">{% trans "All collections" %}</option>
{% minimum_collection_depth collections as min_depth %}
{% for collection in collections %} {% for collection in collections %}
<option value="{{ collection.id|unlocalize }}" {% if collection == current_collection %}selected="selected"{% endif %}>{{ collection.name }}</option> <option value="{{ collection.id|unlocalize }}"
{% if collection == current_collection %}selected="selected"{% endif %}>
{% if request.user.is_superuser %}
{# Superuser may see all collections #}
{% format_collection collection %}
{% else %}
{# Pass the minimum depth of the permitted collections, since user isn't a superuser #}
{% format_collection collection min_depth %}
{% endif %}
</option>
{% endfor %} {% endfor %}
</select> </select>
<span></span> <span></span>

Wyświetl plik

@ -10,6 +10,7 @@ from django.conf import settings
from django.contrib.admin.utils import quote from django.contrib.admin.utils import quote
from django.contrib.humanize.templatetags.humanize import intcomma from django.contrib.humanize.templatetags.humanize import intcomma
from django.contrib.messages.constants import DEFAULT_TAGS as MESSAGE_TAGS from django.contrib.messages.constants import DEFAULT_TAGS as MESSAGE_TAGS
from django.db.models import Min, QuerySet
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.templatetags.static import static from django.templatetags.static import static
@ -27,7 +28,7 @@ from wagtail.admin.search import admin_search_areas
from wagtail.admin.staticfiles import versioned_static as versioned_static_func from wagtail.admin.staticfiles import versioned_static as versioned_static_func
from wagtail.core import hooks from wagtail.core import hooks
from wagtail.core.models import ( from wagtail.core.models import (
CollectionViewRestriction, Page, PageLogEntry, PageViewRestriction, UserPagePermissionsProxy) Collection, CollectionViewRestriction, Page, PageLogEntry, PageViewRestriction, UserPagePermissionsProxy)
from wagtail.core.utils import cautious_slugify as _cautious_slugify from wagtail.core.utils import cautious_slugify as _cautious_slugify
from wagtail.core.utils import accepts_kwarg, camelcase_to_underscore, escape_script from wagtail.core.utils import accepts_kwarg, camelcase_to_underscore, escape_script
from wagtail.users.utils import get_gravatar_url from wagtail.users.utils import get_gravatar_url
@ -603,3 +604,27 @@ def format_action_log_message(log_entry):
if not isinstance(log_entry, PageLogEntry): if not isinstance(log_entry, PageLogEntry):
return '' return ''
return log_action_registry.format_message(log_entry) return log_action_registry.format_message(log_entry)
@register.simple_tag
def format_collection(coll: Collection, min_depth: int = 2) -> str:
"""
Renders a given Collection's name as a formatted string that displays its
hierarchical depth via indentation. If min_depth is supplied, the
Collection's depth is rendered relative to that depth. min_depth defaults
to 2, the depth of the first non-Root Collection.
Example usage: {% format_collection collection min_depth %}
Example output: "&nbsp;&nbsp;&nbsp;&nbsp;&#x21b3 Child Collection"
"""
return coll.get_indented_name(min_depth, html=True)
@register.simple_tag
def minimum_collection_depth(collections: QuerySet) -> int:
"""
Returns the minimum depth of the Collections in the given queryset.
Call this before beginning a loop through Collections that will
use {% format_collection collection min_depth %}.
"""
return collections.aggregate(Min('depth'))['depth__min'] or 2

Wyświetl plik

@ -21,8 +21,8 @@ class Index(IndexView):
header_icon = 'folder-open-1' header_icon = 'folder-open-1'
def get_queryset(self): def get_queryset(self):
# Only return children of the root node, so that the root is not editable # Only return descendants of the root node, so that the root is not editable
return Collection.get_first_root_node().get_children().order_by('name') return Collection.get_first_root_node().get_descendants()
class Create(CreateView): class Create(CreateView):
@ -36,10 +36,10 @@ class Create(CreateView):
header_icon = 'folder-open-1' header_icon = 'folder-open-1'
def save_instance(self): def save_instance(self):
# Always create new collections as children of root
instance = self.form.save(commit=False) instance = self.form.save(commit=False)
root_collection = Collection.get_first_root_node() parent_pk = self.form.data.get('parent')
root_collection.add_child(instance=instance) parent = Collection.objects.get(pk=parent_pk) if parent_pk else Collection.get_first_root_node()
parent.add_child(instance=instance)
return instance return instance
@ -57,9 +57,26 @@ class Edit(EditView):
context_object_name = 'collection' context_object_name = 'collection'
header_icon = 'folder-open-1' header_icon = 'folder-open-1'
def save_instance(self):
instance = self.form.save()
parent_pk = self.form.data.get('parent')
if parent_pk and parent_pk != instance.get_parent().pk:
instance.move(Collection.objects.get(pk=parent_pk), 'sorted-child')
return instance
def form_valid(self, form):
new_parent_pk = int(form.data.get('parent', 0))
old_descendants = list(form.instance.get_descendants(
inclusive=True).values_list('pk', flat=True)
)
if new_parent_pk in old_descendants:
form.add_error('parent', gettext_lazy('Please select another parent'))
return self.form_invalid(form)
return super().form_valid(form)
def get_queryset(self): def get_queryset(self):
# Only return children of the root node, so that the root is not editable # Only return descendants of the root node, so that the root is not editable
return Collection.get_first_root_node().get_children().order_by('name') return Collection.get_first_root_node().get_descendants().order_by('path')
class Delete(DeleteView): class Delete(DeleteView):
@ -74,7 +91,7 @@ class Delete(DeleteView):
def get_queryset(self): def get_queryset(self):
# Only return children of the root node, so that the root is not editable # Only return children of the root node, so that the root is not editable
return Collection.get_first_root_node().get_children().order_by('name') return Collection.get_first_root_node().get_descendants().order_by('path')
def get_collection_contents(self): def get_collection_contents(self):
collection_contents = [ collection_contents = [

Wyświetl plik

@ -14,7 +14,7 @@ from django.core.exceptions import PermissionDenied, ValidationError
from django.core.handlers.base import BaseHandler from django.core.handlers.base import BaseHandler
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Case, Q, Value, When from django.db.models import Q, Value
from django.db.models.expressions import OuterRef, Subquery from django.db.models.expressions import OuterRef, Subquery
from django.db.models.functions import Concat, Lower, Substr from django.db.models.functions import Concat, Lower, Substr
from django.http import Http404 from django.http import Http404
@ -25,7 +25,9 @@ from django.utils import timezone
from django.utils.cache import patch_cache_control from django.utils.cache import patch_cache_control
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.html import format_html
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils.safestring import mark_safe
from django.utils.text import capfirst, slugify from django.utils.text import capfirst, slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from modelcluster.fields import ParentalKey, ParentalManyToManyField from modelcluster.fields import ParentalKey, ParentalManyToManyField
@ -2738,6 +2740,8 @@ class Collection(TreebeardPathFixMixin, MP_Node):
name = models.CharField(max_length=255, verbose_name=_('name')) name = models.CharField(max_length=255, verbose_name=_('name'))
objects = CollectionManager() objects = CollectionManager()
# Tell treebeard to order Collections' paths such that they are ordered by name at each level.
node_order_by = ['name']
def __str__(self): def __str__(self):
return self.name return self.name
@ -2761,13 +2765,33 @@ class Collection(TreebeardPathFixMixin, MP_Node):
"""Return a query set of all collection view restrictions that apply to this collection""" """Return a query set of all collection view restrictions that apply to this collection"""
return CollectionViewRestriction.objects.filter(collection__in=self.get_ancestors(inclusive=True)) return CollectionViewRestriction.objects.filter(collection__in=self.get_ancestors(inclusive=True))
@staticmethod def get_indented_name(self, indentation_start_depth=2, html=False):
def order_for_display(queryset): """
return queryset.annotate( Renders this Collection's name as a formatted string that displays its hierarchical depth via indentation.
display_order=Case( If indentation_start_depth is supplied, the Collection's depth is rendered relative to that depth.
When(depth=1, then=Value('')), indentation_start_depth defaults to 2, the depth of the first non-Root Collection.
default='name') Pass html=True to get a HTML representation, instead of the default plain-text.
).order_by('display_order')
Example text output: " ↳ Pies"
Example HTML output: "&nbsp;&nbsp;&nbsp;&nbsp;&#x21b3 Pies"
"""
display_depth = self.depth - indentation_start_depth
# A Collection with a display depth of 0 or less (Root's can be -1), should have no indent.
if display_depth <= 0:
return self.name
# Indent each level of depth by 4 spaces (the width of the ↳ character in our admin font), then add ↳
# before adding the name.
if html:
# NOTE: &#x21b3 is the hex HTML entity for ↳.
return format_html(
"{indent}{icon} {name}",
indent=mark_safe('&nbsp;' * 4 * display_depth),
icon=mark_safe('&#x21b3'),
name=self.name
)
# Output unicode plain-text version
return "{}{}".format(' ' * 4 * display_depth, self.name)
class Meta: class Meta:
verbose_name = _('collection') verbose_name = _('collection')

Wyświetl plik

@ -2,6 +2,7 @@ from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ngettext
from wagtail.core import hooks from wagtail.core import hooks
from wagtail.core.models import PageViewRestriction from wagtail.core.models import PageViewRestriction
@ -75,3 +76,19 @@ def register_task_permissions():
content_type__app_label='wagtailcore', content_type__app_label='wagtailcore',
codename__in=['add_task', 'change_task', 'delete_task'] codename__in=['add_task', 'change_task', 'delete_task']
) )
@hooks.register('describe_collection_contents')
def describe_collection_children(collection):
descendant_count = collection.get_descendants().count()
if descendant_count:
url = reverse('wagtailadmin_collections:index')
return {
'count': descendant_count,
'count_text': ngettext(
"%(count)s descendant collection",
"%(count)s descendant collections",
descendant_count
) % {'count': descendant_count},
'url': url,
}

Wyświetl plik

@ -4,11 +4,21 @@ from django.utils.translation import gettext_lazy as _
from wagtail.admin import widgets from wagtail.admin import widgets
from wagtail.admin.forms.collections import ( from wagtail.admin.forms.collections import (
BaseCollectionMemberForm, collection_member_permission_formset_factory) BaseCollectionMemberForm, CollectionChoiceField, collection_member_permission_formset_factory)
from wagtail.core.models import Collection
from wagtail.documents.models import Document from wagtail.documents.models import Document
from wagtail.documents.permissions import permission_policy as documents_permission_policy from wagtail.documents.permissions import permission_policy as documents_permission_policy
# Callback to allow us to override the default form field for the collection field
def formfield_for_dbfield(db_field, **kwargs):
if db_field.name == 'collection':
return CollectionChoiceField(queryset=Collection.objects.all(), empty_label=None, **kwargs)
# For all other fields, just call its formfield() method.
return db_field.formfield(**kwargs)
class BaseDocumentForm(BaseCollectionMemberForm): class BaseDocumentForm(BaseCollectionMemberForm):
permission_policy = documents_permission_policy permission_policy = documents_permission_policy
@ -26,6 +36,7 @@ def get_document_form(model):
model, model,
form=BaseDocumentForm, form=BaseDocumentForm,
fields=fields, fields=fields,
formfield_callback=formfield_for_dbfield,
widgets={ widgets={
'tags': widgets.AdminTagWidget, 'tags': widgets.AdminTagWidget,
'file': forms.FileInput() 'file': forms.FileInput()
@ -41,6 +52,7 @@ def get_document_multi_form(model):
model, model,
form=BaseDocumentForm, form=BaseDocumentForm,
fields=fields, fields=fields,
formfield_callback=formfield_for_dbfield,
widgets={ widgets={
'tags': widgets.AdminTagWidget, 'tags': widgets.AdminTagWidget,
'file': forms.FileInput() 'file': forms.FileInput()

Wyświetl plik

@ -30,14 +30,20 @@
<div class="field choice_field select"> <div class="field choice_field select">
<label for="id_adddocument_collection">{% trans "Add to collection:" %}</label> <label for="id_adddocument_collection">{% trans "Add to collection:" %}</label>
<div class="field-content"> <div class="field-content">
<div class="input"> <select id="id_adddocument_collection" name="collection">
<select id="id_adddocument_collection" name="collection"> {% minimum_collection_depth collections as min_depth %}
{% for collection in collections %} {% for collection in collections %}
<option value="{{ collection.id|unlocalize }}">{{ collection.name }}</option> <option value="{{ collection.id|unlocalize }}">
{% endfor %} {% if request.user.is_superuser %}
</select> {# Superuser may see all collections. #}
<span></span> {% format_collection collection %}
</div> {% else %}
{# Pass the minimum depth of the permitted collections, since user isn't a superuser #}
{% format_collection collection min_depth %}
{% endif %}
</option>
{% endfor %}
</select>
</div> </div>
</div> </div>
{% endif %} {% endif %}

Wyświetl plik

@ -93,8 +93,6 @@ def chooser(request):
collections = Collection.objects.all() collections = Collection.objects.all()
if len(collections) < 2: if len(collections) < 2:
collections = None collections = None
else:
collections = Collection.order_for_display(collections)
documents = documents.order_by('-created_at') documents = documents.order_by('-created_at')
documents_exist = documents.exists() documents_exist = documents.exists()

Wyświetl plik

@ -66,8 +66,6 @@ def index(request):
) )
if len(collections) < 2: if len(collections) < 2:
collections = None collections = None
else:
collections = Collection.order_for_display(collections)
# Create response # Create response
if request.is_ajax(): if request.is_ajax():

Wyświetl plik

@ -8,7 +8,6 @@ from django.views.decorators.http import require_POST
from django.views.decorators.vary import vary_on_headers from django.views.decorators.vary import vary_on_headers
from wagtail.admin.auth import PermissionPolicyChecker from wagtail.admin.auth import PermissionPolicyChecker
from wagtail.core.models import Collection
from wagtail.search.backends import get_search_backends from wagtail.search.backends import get_search_backends
from .. import get_document_model from .. import get_document_model
@ -26,11 +25,9 @@ def add(request):
DocumentMultiForm = get_document_multi_form(Document) DocumentMultiForm = get_document_multi_form(Document)
collections = permission_policy.collections_user_has_permission_for(request.user, 'add') collections = permission_policy.collections_user_has_permission_for(request.user, 'add')
if len(collections) > 1: if len(collections) < 2:
collections_to_choose = Collection.order_for_display(collections)
else:
# no need to show a collections chooser # no need to show a collections chooser
collections_to_choose = None collections = None
if request.method == 'POST': if request.method == 'POST':
if not request.is_ajax(): if not request.is_ajax():
@ -86,7 +83,7 @@ def add(request):
return TemplateResponse(request, 'wagtaildocs/multiple/add.html', { return TemplateResponse(request, 'wagtaildocs/multiple/add.html', {
'help_text': form.fields['file'].help_text, 'help_text': form.fields['file'].help_text,
'collections': collections_to_choose, 'collections': collections,
'form_media': form.media, 'form_media': form.media,
}) })

Wyświetl plik

@ -5,18 +5,21 @@ from django.utils.translation import gettext as _
from wagtail.admin import widgets from wagtail.admin import widgets
from wagtail.admin.forms.collections import ( from wagtail.admin.forms.collections import (
BaseCollectionMemberForm, collection_member_permission_formset_factory) BaseCollectionMemberForm, CollectionChoiceField, collection_member_permission_formset_factory)
from wagtail.core.models import Collection
from wagtail.images.fields import WagtailImageField from wagtail.images.fields import WagtailImageField
from wagtail.images.formats import get_image_formats from wagtail.images.formats import get_image_formats
from wagtail.images.models import Image from wagtail.images.models import Image
from wagtail.images.permissions import permission_policy as images_permission_policy from wagtail.images.permissions import permission_policy as images_permission_policy
# Callback to allow us to override the default form field for the image file field # Callback to allow us to override the default form field for the image file field and collection field.
def formfield_for_dbfield(db_field, **kwargs): def formfield_for_dbfield(db_field, **kwargs):
# Check if this is the file field # Check if this is the file field
if db_field.name == 'file': if db_field.name == 'file':
return WagtailImageField(label=capfirst(db_field.verbose_name), **kwargs) return WagtailImageField(label=capfirst(db_field.verbose_name), **kwargs)
elif db_field.name == 'collection':
return CollectionChoiceField(queryset=Collection.objects.all(), empty_label=None, **kwargs)
# For all other fields, just call its formfield() method. # For all other fields, just call its formfield() method.
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)

Wyświetl plik

@ -30,14 +30,20 @@
<div class="field choice_field select"> <div class="field choice_field select">
<label for="id_addimage_collection">{% trans "Add to collection:" %}</label> <label for="id_addimage_collection">{% trans "Add to collection:" %}</label>
<div class="field-content"> <div class="field-content">
<div class="input"> <select id="id_addimage_collection" name="collection">
<select id="id_addimage_collection" name="collection"> {% minimum_collection_depth collections as min_depth %}
{% for collection in collections %} {% for collection in collections %}
<option value="{{ collection.id|unlocalize }}">{{ collection.name }}</option> <option value="{{ collection.id|unlocalize }}">
{% endfor %} {% if request.user.is_superuser %}
</select> {# Superuser may see all collections. #}
<span></span> {% format_collection collection %}
</div> {% else %}
{# Pass the minimum depth of the permitted collections, since user isn't a superuser #}
{% format_collection collection min_depth %}
{% endif %}
</option>
{% endfor %}
</select>
</div> </div>
</div> </div>
{% endif %} {% endif %}

Wyświetl plik

@ -57,8 +57,6 @@ def get_chooser_context(request):
collections = Collection.objects.all() collections = Collection.objects.all()
if len(collections) < 2: if len(collections) < 2:
collections = None collections = None
else:
collections = Collection.order_for_display(collections)
return { return {
'searchform': SearchForm(), 'searchform': SearchForm(),

Wyświetl plik

@ -76,8 +76,6 @@ def index(request):
) )
if len(collections) < 2: if len(collections) < 2:
collections = None collections = None
else:
collections = Collection.order_for_display(collections)
# Create response # Create response
if request.is_ajax(): if request.is_ajax():

Wyświetl plik

@ -10,7 +10,6 @@ from django.views.decorators.http import require_POST
from django.views.decorators.vary import vary_on_headers from django.views.decorators.vary import vary_on_headers
from wagtail.admin.auth import PermissionPolicyChecker from wagtail.admin.auth import PermissionPolicyChecker
from wagtail.core.models import Collection
from wagtail.images import get_image_model from wagtail.images import get_image_model
from wagtail.images.fields import ALLOWED_EXTENSIONS from wagtail.images.fields import ALLOWED_EXTENSIONS
from wagtail.images.forms import get_image_form from wagtail.images.forms import get_image_form
@ -46,11 +45,9 @@ def add(request):
ImageForm = get_image_form(Image) ImageForm = get_image_form(Image)
collections = permission_policy.collections_user_has_permission_for(request.user, 'add') collections = permission_policy.collections_user_has_permission_for(request.user, 'add')
if len(collections) > 1: if len(collections) < 2:
collections_to_choose = Collection.order_for_display(collections)
else:
# no need to show a collections chooser # no need to show a collections chooser
collections_to_choose = None collections = None
if request.method == 'POST': if request.method == 'POST':
if not request.is_ajax(): if not request.is_ajax():
@ -128,7 +125,7 @@ def add(request):
'allowed_extensions': ALLOWED_EXTENSIONS, 'allowed_extensions': ALLOWED_EXTENSIONS,
'error_max_file_size': form.fields['file'].error_messages['file_too_large_unknown_size'], 'error_max_file_size': form.fields['file'].error_messages['file_too_large_unknown_size'],
'error_accepted_file_types': form.fields['file'].error_messages['invalid_image_extension'], 'error_accepted_file_types': form.fields['file'].error_messages['invalid_image_extension'],
'collections': collections_to_choose, 'collections': collections,
'form_media': form.media, 'form_media': form.media,
}) })