You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
10 KiB
281 lines
10 KiB
import re
|
|
from collections import Counter
|
|
|
|
from django.contrib import admin
|
|
from django.db.models import Q
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
|
from django.contrib.sites.models import Site
|
|
from django.core.exceptions import ValidationError
|
|
from django.forms import ModelForm, ModelMultipleChoiceField
|
|
from django.forms.models import BaseInlineFormSet
|
|
from django.template import Template, TemplateSyntaxError, \
|
|
TemplateDoesNotExist, loader
|
|
from django.template.loader import render_to_string
|
|
from django.forms.widgets import Select
|
|
|
|
from django.contrib.admin.templatetags.admin_static import static
|
|
|
|
from admin_extend.extend import registered_form, extend_registered, \
|
|
add_bidirectional_m2m
|
|
|
|
from models import SmartSnippet, SmartSnippetVariable, DropDownVariable, clean_variable_name
|
|
from settings import (
|
|
shared_sites, include_orphan, restrict_user, handle_permissions_checks,
|
|
custom_widgets_resources, USE_BOOTSTRAP_ACE)
|
|
from widgets_pool import widget_pool
|
|
|
|
|
|
class SnippetForm(ModelForm):
|
|
include_orphan = include_orphan
|
|
use_ace_theme = USE_BOOTSTRAP_ACE
|
|
|
|
class Meta:
|
|
model = SmartSnippet
|
|
exclude = ()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if 'sites' in self.base_fields:
|
|
# disallow django to validate if empty since it is done
|
|
# in the clean sites method
|
|
self.base_fields['sites'].required = False
|
|
super(SnippetForm, self).__init__(*args, **kwargs)
|
|
|
|
def clean_sites(self):
|
|
empty_sites = Site.objects.none()
|
|
self.cleaned_data['sites'] = self.cleaned_data.get(
|
|
'sites', empty_sites) or empty_sites
|
|
|
|
def ids_list(queryset):
|
|
return list(queryset.values_list('id', flat=True))
|
|
|
|
all_in_form = self.base_fields['sites'].queryset
|
|
assigned_in_form = ids_list(self.cleaned_data['sites'])
|
|
unassigned_in_form = ids_list(
|
|
all_in_form.exclude(id__in=assigned_in_form))
|
|
|
|
if self.instance.pk:
|
|
assigned_and_unchanged = ids_list(
|
|
self.instance.sites.exclude(id__in=unassigned_in_form))
|
|
all_assigned = assigned_in_form + assigned_and_unchanged
|
|
else:
|
|
all_assigned = assigned_in_form
|
|
|
|
self.cleaned_data['sites'] = Site.objects.filter(id__in=all_assigned)
|
|
|
|
if not self.include_orphan and not self.cleaned_data['sites']:
|
|
raise ValidationError('This field is required.')
|
|
return self.cleaned_data['sites']
|
|
|
|
def clean_template_code(self):
|
|
code = self.cleaned_data.get('template_code', None)
|
|
if not code:
|
|
return code
|
|
try:
|
|
Template(code)
|
|
except TemplateSyntaxError, e:
|
|
raise ValidationError(e)
|
|
return code
|
|
|
|
def clean_template_path(self):
|
|
path = self.cleaned_data.get('template_path', None)
|
|
if not path:
|
|
return path
|
|
try:
|
|
loader.get_template(path)
|
|
except TemplateDoesNotExist, e:
|
|
raise ValidationError(e)
|
|
return path
|
|
|
|
def clean(self):
|
|
clean_result = super(SnippetForm, self).clean()
|
|
self.validate_unique_variable_names()
|
|
return clean_result
|
|
|
|
def validate_unique_variable_names(self):
|
|
""" Validates name uniqueness over all variable inlines. """
|
|
all_variable_names = [clean_variable_name(value)
|
|
for key, value in self.data.dict().iteritems()
|
|
if re.match(r"variables[0-9-]*name", key)]
|
|
duplicate_variable_names = [var_name for var_name, count
|
|
in Counter(all_variable_names).iteritems()
|
|
if count > 1]
|
|
|
|
if duplicate_variable_names:
|
|
if len(duplicate_variable_names) == 1:
|
|
raise ValidationError(
|
|
'The variable name "{}" is used multiple times.'.format(duplicate_variable_names.pop()))
|
|
raise ValidationError('The variable names "{}" are used multiple times.'.format(
|
|
', '.join(duplicate_variable_names)))
|
|
|
|
|
|
class SnippetVariablesFormSet(BaseInlineFormSet):
|
|
def get_queryset(self):
|
|
if not hasattr(self, '_queryset'):
|
|
available_widgets = [widget.__name__ for widget in widget_pool.get_all_widgets()]
|
|
qs = super(SnippetVariablesFormSet, self).get_queryset().filter(widget__in=available_widgets)
|
|
self._queryset = qs
|
|
return self._queryset
|
|
|
|
|
|
class SnippetVariablesAdmin(admin.StackedInline):
|
|
model = SmartSnippetVariable
|
|
extra = 0
|
|
readonly_fields = ['predefined_widgets']
|
|
|
|
def predefined_widgets(self, ssvar):
|
|
return render_to_string(
|
|
'smartsnippets/predefined_widgets.html',
|
|
{'widgets': custom_widgets_resources,
|
|
'snippet_var': ssvar})
|
|
|
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
|
if db_field.name == 'widget':
|
|
kwargs['widget'] = Select(choices=tuple([(x.__name__, x.name) for x in widget_pool.get_all_widgets()]))
|
|
return super(SnippetVariablesAdmin,self).formfield_for_dbfield(db_field, **kwargs)
|
|
|
|
@staticmethod
|
|
def _fieldsets(required):
|
|
return (
|
|
(None, {
|
|
'fields': (required, )
|
|
}),
|
|
('Advanced', {
|
|
'fields': (('resources', 'predefined_widgets'), ),
|
|
'classes': ('collapse', )
|
|
}),
|
|
)
|
|
|
|
|
|
class RegularSnippetVariablesAdmin(SnippetVariablesAdmin):
|
|
formset = SnippetVariablesFormSet
|
|
fieldsets = SnippetVariablesAdmin._fieldsets(('name', 'widget'))
|
|
|
|
|
|
class DropDownVariableAdmin(SnippetVariablesAdmin):
|
|
model = DropDownVariable
|
|
exclude = ('widget',)
|
|
fieldsets = SnippetVariablesAdmin._fieldsets(('name', 'choices'))
|
|
|
|
|
|
class SnippetAdmin(admin.ModelAdmin):
|
|
inlines = [RegularSnippetVariablesAdmin, DropDownVariableAdmin]
|
|
shared_sites = shared_sites
|
|
include_orphan = include_orphan
|
|
restrict_user = restrict_user
|
|
|
|
list_filter = ('sites__name', )
|
|
list_display = ('name', 'site_list')
|
|
search_fields = ['name']
|
|
form = SnippetForm
|
|
change_form_template = 'smartsnippets/change_form.html'
|
|
filter_horizontal = ('sites', )
|
|
|
|
class Media:
|
|
js = ("admin/js/SmartSnippets.Variables.js",
|
|
"admin/js/SmartSnippets.PredefinedWidgets.js",)
|
|
|
|
@property
|
|
def media(self):
|
|
|
|
media_obj = super(SnippetAdmin, self).media
|
|
|
|
if not USE_BOOTSTRAP_ACE:
|
|
media_obj.add_css({
|
|
'all': (
|
|
static('admin/css/forms.css'),
|
|
static('admin/css/smartsnippets-extra.css'),)
|
|
})
|
|
return media_obj
|
|
|
|
def site_list(self, template):
|
|
return ", ".join([site.name for site in template.sites.all()])
|
|
site_list.short_description = 'sites'
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
if not handle_permissions_checks:
|
|
return super(SnippetAdmin, self)\
|
|
.get_readonly_fields(request, obj=obj)
|
|
ro = self.form.base_fields.keys()
|
|
if request.user.is_superuser or obj is None:
|
|
return []
|
|
if self.restrict_user and self.shared_sites:
|
|
if obj.sites.filter(name__in=self.shared_sites):
|
|
return ro
|
|
return []
|
|
|
|
def has_delete_permission(self, request, obj=None):
|
|
if not handle_permissions_checks:
|
|
return super(SnippetAdmin, self)\
|
|
.has_delete_permission(request, obj=obj)
|
|
if request.user.is_superuser or obj is None:
|
|
return True
|
|
if self.restrict_user and self.shared_sites:
|
|
return not bool(obj.sites.filter(name__in=self.shared_sites))
|
|
return True
|
|
|
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
|
if not handle_permissions_checks:
|
|
return super(SnippetAdmin, self)\
|
|
.formfield_for_manytomany(db_field, request, **kwargs)
|
|
if db_field.name == "sites":
|
|
f = Q()
|
|
if not request.user.is_superuser:
|
|
if self.restrict_user:
|
|
f |= Q(globalpagepermission__user=request.user)
|
|
f |= Q(globalpagepermission__group__user=request.user)
|
|
kwargs["queryset"] = Site.objects.filter(f).distinct()
|
|
return (super(SnippetAdmin, self)
|
|
.formfield_for_manytomany(db_field, request, **kwargs))
|
|
|
|
def get_queryset(self, request):
|
|
q = super(SnippetAdmin, self).get_queryset(request)
|
|
if not handle_permissions_checks:
|
|
return q
|
|
f = Q()
|
|
if not request.user.is_superuser:
|
|
if self.restrict_user:
|
|
f |= Q(sites__globalpagepermission__user=request.user)
|
|
f |= Q(sites__globalpagepermission__group__user=request.user)
|
|
if self.shared_sites:
|
|
f |= Q(sites__name__in=self.shared_sites)
|
|
if self.include_orphan:
|
|
f |= Q(sites__isnull=True)
|
|
|
|
return q.filter(f).distinct()
|
|
|
|
|
|
admin.site.register(SmartSnippet, SnippetAdmin)
|
|
|
|
|
|
@extend_registered
|
|
class ExtendedSiteAdminForm(add_bidirectional_m2m(registered_form(Site))):
|
|
|
|
snippets = ModelMultipleChoiceField(
|
|
queryset=SmartSnippet.objects.all(),
|
|
required=False,
|
|
widget=FilteredSelectMultiple(
|
|
verbose_name='Snippets',
|
|
is_stacked=False
|
|
)
|
|
)
|
|
|
|
def _get_bidirectional_m2m_fields(self):
|
|
return super(ExtendedSiteAdminForm, self).\
|
|
_get_bidirectional_m2m_fields() + [('snippets', 'smartsnippet_set')]
|
|
|
|
def clean_snippets(self):
|
|
assigned_snippets = self.cleaned_data['snippets']
|
|
if self.instance.pk is None or include_orphan:
|
|
return assigned_snippets
|
|
pks = [s.pk for s in assigned_snippets]
|
|
# snippets that were previously assigned to this site, but got unassigned
|
|
unassigned_snippets = self.instance.smartsnippet_set.exclude(pk__in=pks)
|
|
snippets_with_no_sites = []
|
|
for snippet in unassigned_snippets:
|
|
if snippet.sites.count() == 1:
|
|
snippets_with_no_sites.append(snippet)
|
|
if snippets_with_no_sites:
|
|
raise ValidationError(
|
|
"Following snippets will remain with no sites assigned: %s" %
|
|
", ".join(s.name for s in snippets_with_no_sites))
|
|
return assigned_snippets
|
|
|