465 lines
16 KiB
Python
465 lines
16 KiB
Python
# combo - content management system
|
|
# Copyright (C) 2014-2016 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import re
|
|
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import Group
|
|
from django.core.exceptions import ValidationError
|
|
from django.forms import formset_factory
|
|
from django.template import Template, TemplateSyntaxError
|
|
from django.template.loader import TemplateDoesNotExist, get_template
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from combo import utils
|
|
from combo.apps.wcs.utils import get_wcs_matching_card_model, get_wcs_options
|
|
from combo.data.forms import get_page_choices
|
|
from combo.data.models import Page, ParentContentCell, SiteSettings, compile_sub_slug
|
|
|
|
from .fields import ImageIncludingSvgField
|
|
|
|
|
|
@utils.cache_during_request
|
|
def get_groups_as_choices():
|
|
return [(x.id, x.name) for x in Group.objects.all().order_by('name')]
|
|
|
|
|
|
def get_template_name_choices(with_models=False):
|
|
def template_exists(template):
|
|
try:
|
|
get_template(settings.COMBO_PUBLIC_TEMPLATES[template].get('template'))
|
|
except TemplateDoesNotExist:
|
|
return False
|
|
return True
|
|
|
|
templates = [(x[0], x[1]['name']) for x in settings.COMBO_PUBLIC_TEMPLATES.items()]
|
|
templates = [x for x in templates if template_exists(x[0])]
|
|
templates.sort(key=lambda x: x[1])
|
|
|
|
if not with_models or settings.COMBO_PUBLIC_TEMPLATES_ROOT_PAGE is None:
|
|
return templates
|
|
|
|
try:
|
|
model_root_page = Page.objects.get(slug=settings.COMBO_PUBLIC_TEMPLATES_ROOT_PAGE)
|
|
except Page.DoesNotExist:
|
|
return templates
|
|
|
|
templates.extend([('model_%s' % p.slug, p.title) for p in model_root_page.get_descendants()])
|
|
templates.sort(key=lambda x: x[1])
|
|
return templates
|
|
|
|
|
|
def build_tab_is_not_default(form):
|
|
if getattr(form, 'is_not_default', None) is True:
|
|
return True
|
|
for field_name, field in form.fields.items():
|
|
if field.initial is not None:
|
|
if field.initial != form.initial.get(field_name):
|
|
return True
|
|
else:
|
|
if bool(form.initial.get(field_name)):
|
|
return True
|
|
return False
|
|
|
|
|
|
class PageDuplicateForm(forms.Form):
|
|
title = forms.CharField(label=_('New title'), max_length=150, required=False)
|
|
|
|
|
|
class PageExportForm(forms.Form):
|
|
with_subpages = forms.BooleanField(label=_('Also export subpages'), required=False)
|
|
|
|
|
|
class PageAddForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('title', 'template_name')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.request = kwargs.pop('request')
|
|
self.parent = kwargs.pop('parent', None)
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['template_name'].widget = forms.Select(
|
|
choices=get_template_name_choices(with_models=True)
|
|
)
|
|
self.fields['template_name'].initial = settings.COMBO_DEFAULT_PUBLIC_TEMPLATE
|
|
|
|
def save(self, *args, **kwargs):
|
|
template_name = self.cleaned_data['template_name']
|
|
if template_name.startswith('model_'):
|
|
try:
|
|
model_page = Page.objects.get(slug=template_name[6:])
|
|
except Page.DoesNotExist:
|
|
pass
|
|
else:
|
|
# model page, create page by duplication
|
|
self.instance = model_page.duplicate(title=self.cleaned_data['title'], parent=self.parent)
|
|
|
|
if self.instance.pk is None:
|
|
# not a model page, create page from form
|
|
self.instance = super().save(commit=False, *args, **kwargs)
|
|
self.instance.parent = self.parent
|
|
self.instance.save()
|
|
|
|
if self.instance.slug != 'index' or self.parent:
|
|
for placeholder in self.instance.get_placeholders(request=self.request):
|
|
if not placeholder.acquired:
|
|
continue
|
|
ParentContentCell.objects.create(page=self.instance, placeholder=placeholder.key, order=0)
|
|
|
|
return self.instance
|
|
|
|
|
|
class PageRestrictedAddForm(PageAddForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('title', 'template_name', 'parent')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
group_ids = [x.id for x in self.request.user.groups.all()]
|
|
self.fields['parent'].queryset = Page.objects.filter(subpages_edit_role_id__in=group_ids)
|
|
self.fields['parent'].required = True
|
|
self.fields['parent'].empty_label = None
|
|
|
|
def save(self, *args, **kwargs):
|
|
self.parent = self.cleaned_data.get('parent')
|
|
return super().save(*args, **kwargs)
|
|
|
|
|
|
class PageEditTitleForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('title',)
|
|
|
|
|
|
class PageEditSlugForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('slug', 'sub_slug')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if self.instance.sub_slug:
|
|
result = get_wcs_matching_card_model(self.instance.sub_slug)
|
|
if result:
|
|
del self.fields['sub_slug']
|
|
|
|
def clean_slug(self):
|
|
value = self.cleaned_data.get('slug')
|
|
if self.instance.slug == value:
|
|
return value
|
|
if Page.objects.filter(slug=value, parent_id=self.instance.parent_id).count() > 0:
|
|
raise ValidationError(_('Slug must be unique'), code='unique')
|
|
return value
|
|
|
|
def clean_sub_slug(self):
|
|
value = self.cleaned_data.get('sub_slug')
|
|
if not value:
|
|
return value
|
|
|
|
try:
|
|
compile_sub_slug(value)
|
|
except re.error:
|
|
raise ValidationError(_('Bad Regular expression.'), code='bad_regex')
|
|
|
|
return value
|
|
|
|
|
|
class PageEditLinkedCardForm(forms.ModelForm):
|
|
carddef_reference = forms.ChoiceField(
|
|
label=_('Linked card model'),
|
|
required=False,
|
|
)
|
|
|
|
class Meta:
|
|
model = Page
|
|
fields = []
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
card_models = get_wcs_options('/api/cards/@list')
|
|
self.fields['carddef_reference'].choices = [('', '-----')] + card_models
|
|
self.help_texts = {}
|
|
for card_model in card_models:
|
|
sub_slug = card_model[0].split(':')[1].replace('-', '_')
|
|
self.help_texts[card_model[0]] = mark_safe(
|
|
_('The card identifier will be available in the variable <code>%s_id</code>.') % sub_slug
|
|
)
|
|
|
|
if self.instance.sub_slug:
|
|
result = get_wcs_matching_card_model(self.instance.sub_slug)
|
|
if result:
|
|
self.initial['carddef_reference'] = result[0]
|
|
|
|
def save(self, commit=True, *args, **kwargs):
|
|
super().save(commit=False, *args, **kwargs)
|
|
|
|
if self.cleaned_data.get('carddef_reference'):
|
|
self.instance.sub_slug = '%s_id' % self.cleaned_data['carddef_reference'].split(':')[1]
|
|
else:
|
|
self.instance.sub_slug = ''
|
|
|
|
if commit:
|
|
self.instance.save()
|
|
return self.instance
|
|
|
|
|
|
class PageEditDescriptionForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('description',)
|
|
|
|
|
|
class PageEditPictureForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('picture',)
|
|
field_classes = {'picture': ImageIncludingSvgField}
|
|
|
|
|
|
class PageEditExtraVariableForm(forms.Form):
|
|
key = forms.CharField(label=_('Variable name'), required=False)
|
|
value = forms.CharField(
|
|
label=_('Value template'), widget=forms.TextInput(attrs={'size': 60}), required=False
|
|
)
|
|
|
|
|
|
PageEditExtraVariablesFormSet = formset_factory(PageEditExtraVariableForm)
|
|
|
|
|
|
class PageVisibilityForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('public', 'groups')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['groups'].queryset = Group.objects.order_by('name')
|
|
|
|
|
|
class PageSelectTemplateForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('template_name',)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['template_name'].widget = forms.Select(choices=get_template_name_choices())
|
|
|
|
|
|
class PageEditRedirectionForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Page
|
|
fields = ('redirect_url',)
|
|
|
|
def clean_redirect_url(self):
|
|
value = self.cleaned_data.get('redirect_url')
|
|
if value:
|
|
try:
|
|
Template(value)
|
|
except TemplateSyntaxError as e:
|
|
raise ValidationError(_('syntax error: %s') % e)
|
|
return value
|
|
|
|
def save(self, *args, **kwargs):
|
|
page = super().save(*args, **kwargs)
|
|
page.redirect_url = page.redirect_url.strip()
|
|
page.save()
|
|
return page
|
|
|
|
|
|
class PageEditRolesForm(forms.ModelForm):
|
|
edit_role = forms.ModelChoiceField(
|
|
label=_('Edit Role'), required=False, queryset=Group.objects.all().order_by('name')
|
|
)
|
|
subpages_edit_role = forms.ModelChoiceField(
|
|
label=_('Subpages Edit Role'), required=False, queryset=Group.objects.all().order_by('name')
|
|
)
|
|
|
|
class Meta:
|
|
model = Page
|
|
fields = (
|
|
'edit_role',
|
|
'subpages_edit_role',
|
|
)
|
|
|
|
|
|
class PageEditIncludeInNavigationForm(forms.ModelForm):
|
|
include_in_navigation = forms.BooleanField(label=_('Include in navigation menus'), required=False)
|
|
apply_to_subpages = forms.BooleanField(label=_('Apply to subpages'), required=False)
|
|
|
|
class Meta:
|
|
model = Page
|
|
fields = []
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
instance = kwargs['instance']
|
|
initial = kwargs.pop('initial', {})
|
|
initial['include_in_navigation'] = not instance.exclude_from_navigation
|
|
super().__init__(initial=initial, *args, **kwargs)
|
|
if initial['include_in_navigation'] or not instance.has_children():
|
|
del self.fields['apply_to_subpages']
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
if self.cleaned_data.get('apply_to_subpages'):
|
|
subpages = self.instance.get_descendants(include_myself=True)
|
|
subpages.update(exclude_from_navigation=bool(not self.cleaned_data['include_in_navigation']))
|
|
else:
|
|
self.instance.exclude_from_navigation = not self.cleaned_data['include_in_navigation']
|
|
self.instance.save()
|
|
return self.instance
|
|
|
|
|
|
class CellVisibilityForm(forms.Form):
|
|
visibility = forms.ChoiceField(
|
|
label=_('Visibility'),
|
|
choices=[
|
|
('all', _('Everybody')),
|
|
('logged', _('Logged users')),
|
|
('unlogged', _('Unlogged users')),
|
|
('groups-any', _('Users with one of these groups')),
|
|
('groups-none', _('Users with none of these groups')),
|
|
],
|
|
initial='all',
|
|
)
|
|
groups = forms.MultipleChoiceField(label=_('Groups'), required=False, choices=get_groups_as_choices)
|
|
condition = forms.CharField(
|
|
label=_('Display condition'),
|
|
widget=forms.TextInput(attrs={'class': 'text-wide'}),
|
|
max_length=1000,
|
|
required=False,
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.instance = instance = kwargs.pop('instance')
|
|
initial = kwargs.get('initial')
|
|
initial['groups'] = [x.id for x in instance.groups.all()]
|
|
if instance.public:
|
|
if instance.restricted_to_unlogged:
|
|
initial['visibility'] = 'unlogged'
|
|
else:
|
|
initial['visibility'] = 'all'
|
|
else:
|
|
initial['visibility'] = 'logged'
|
|
if initial['groups']:
|
|
if instance.restricted_to_unlogged:
|
|
initial['visibility'] = 'groups-none'
|
|
else:
|
|
initial['visibility'] = 'groups-any'
|
|
initial['condition'] = instance.condition
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def clean_condition(self):
|
|
condition = self.cleaned_data['condition']
|
|
if not condition:
|
|
return condition
|
|
try:
|
|
Template('{%% if %s %%}OK{%% endif %%}' % condition)
|
|
except TemplateSyntaxError:
|
|
raise ValidationError(_('Invalid syntax.'))
|
|
|
|
return condition
|
|
|
|
def save(self):
|
|
if self.cleaned_data['visibility'] == 'all':
|
|
self.instance.public = True
|
|
self.instance.restricted_to_unlogged = False
|
|
self.instance.groups.clear()
|
|
elif self.cleaned_data['visibility'] == 'logged':
|
|
self.instance.public = False
|
|
self.instance.restricted_to_unlogged = False
|
|
self.instance.groups.clear()
|
|
elif self.cleaned_data['visibility'] == 'unlogged':
|
|
self.instance.public = True
|
|
self.instance.restricted_to_unlogged = True
|
|
self.instance.groups.clear()
|
|
elif self.cleaned_data['visibility'] == 'groups-any':
|
|
self.instance.public = False
|
|
self.instance.restricted_to_unlogged = False
|
|
self.instance.groups.set(self.cleaned_data['groups'])
|
|
elif self.cleaned_data['visibility'] == 'groups-none':
|
|
self.instance.public = False
|
|
self.instance.restricted_to_unlogged = True
|
|
self.instance.groups.set(self.cleaned_data['groups'])
|
|
self.instance.condition = self.cleaned_data.get('condition')
|
|
self.instance.save()
|
|
return self.instance
|
|
|
|
|
|
class CellDuplicateForm(forms.Form):
|
|
target_page = forms.ModelChoiceField(
|
|
label=_('Target page'),
|
|
queryset=Page.objects.all(),
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.request = kwargs.pop('request')
|
|
self.current_page = kwargs.pop('current_page')
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['target_page'].widget = forms.Select(choices=get_page_choices())
|
|
self.fields['target_page'].initial = self.current_page
|
|
|
|
|
|
class SiteImportForm(forms.Form):
|
|
site_file = forms.FileField(label=_('Site Export File'))
|
|
|
|
|
|
class SiteExportForm(forms.Form):
|
|
pages = forms.BooleanField(label=_('Pages'), required=False, initial=True)
|
|
cartography = forms.BooleanField(label=_('Maps'), required=False, initial=True)
|
|
pwa = forms.BooleanField(label=_('Mobile Application (PWA)'), required=False, initial=True)
|
|
assets = forms.BooleanField(label=_('Assets'), required=False, initial=True)
|
|
asset_files = forms.BooleanField(label=_('Assets Files'), required=False, initial=False)
|
|
payment = forms.BooleanField(label=_('Online Payment'), required=False, initial=True)
|
|
site_settings = forms.BooleanField(label=_('Site Settings'), required=False, initial=True)
|
|
|
|
|
|
class SiteSettingsForm(forms.ModelForm):
|
|
class Meta:
|
|
model = SiteSettings
|
|
fields = '__all__'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['welcome_page'].widget.template_name = 'combo/widgets/select_with_other_option.html'
|
|
self.fields['welcome_page'].widget.attrs['class'] = 'page-selector'
|
|
self.fields['initial_login_page'].widget.template_name = 'combo/widgets/select_with_other_option.html'
|
|
self.fields['initial_login_page'].widget.attrs['class'] = 'page-selector'
|
|
self.fields['welcome_page'].queryset = self.fields['welcome_page'].queryset.filter(public=True)
|
|
|
|
|
|
class PlaceholderOptionsForm(forms.Form):
|
|
fx_grid_layout = forms.ChoiceField(
|
|
label=_('Grid Layout'),
|
|
required=False,
|
|
choices=[
|
|
('', _('Manual')),
|
|
('fx-grid', _('1 column')),
|
|
('fx-grid--t2', _('2 columns')),
|
|
('fx-grid--t3', _('3 columns')),
|
|
],
|
|
)
|
|
|
|
|
|
class PageSnapshotSaveForm(forms.Form):
|
|
label = forms.CharField(label=_('Label'), max_length=150, required=True)
|