1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
"""Page CMS forms""" |
3
|
|
|
from django import forms |
4
|
|
|
from django.template.defaultfilters import slugify |
5
|
|
|
from django.utils.translation import ugettext_lazy as _ |
6
|
|
|
from django.conf import settings as global_settings |
7
|
|
|
|
8
|
|
|
from pages import settings |
9
|
|
|
from pages.models import Page, Content |
10
|
|
|
|
11
|
|
|
from pages.urlconf_registry import get_choices |
12
|
|
|
from pages.widgets import LanguageChoiceWidget |
13
|
|
|
import collections |
14
|
|
|
|
15
|
|
|
error_dict = { |
16
|
|
|
'another_page_error': _('Another page with this slug already exists'), |
17
|
|
|
'sibling_position_error': _('A sibling with this slug already exists at the targeted position'), |
18
|
|
|
'child_error': _('A child with this slug already exists at the targeted position'), |
19
|
|
|
'sibling_error': _('A sibling with this slug already exists'), |
20
|
|
|
'sibling_root_error': _('A sibling with this slug already exists at the root level'), |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
def automatic_slug_renaming(slug, is_slug_safe): |
24
|
|
|
"""Helper to add numbers to slugs""" |
25
|
|
|
|
26
|
|
|
if not isinstance(is_slug_safe, collections.Callable): |
27
|
|
|
raise TypeError('is_slug_safe must be callable') |
28
|
|
|
|
29
|
|
|
if is_slug_safe(slug): |
30
|
|
|
return slug |
31
|
|
|
|
32
|
|
|
count = 2 |
33
|
|
|
new_slug = slug + "-" + str(count) |
34
|
|
|
while not is_slug_safe(new_slug): |
35
|
|
|
count = count + 1 |
36
|
|
|
new_slug = slug + "-" + str(count) |
37
|
|
|
return new_slug |
38
|
|
|
|
39
|
|
|
def unique_slug_required(form, slug): |
40
|
|
|
"""Enforce a unique slug accross all pages and websistes.""" |
41
|
|
|
|
42
|
|
|
if hasattr(form, 'instance') and form.instance.id: |
43
|
|
|
if Content.objects.exclude(page=form.instance).filter( |
44
|
|
|
body=slug, type="slug").count(): |
45
|
|
|
raise forms.ValidationError(error_dict['another_page_error']) |
46
|
|
|
elif Content.objects.filter(body=slug, type="slug").count(): |
47
|
|
|
raise forms.ValidationError(error_dict['another_page_error']) |
48
|
|
|
return slug |
49
|
|
|
|
50
|
|
|
def intersect_sites_method(form): |
51
|
|
|
"""Return a method to intersect sites.""" |
52
|
|
|
if settings.PAGE_USE_SITE_ID: |
53
|
|
|
if settings.PAGE_HIDE_SITES: |
54
|
|
|
site_ids = [global_settings.SITE_ID] |
55
|
|
|
else: |
56
|
|
|
site_ids = [int(x) for x in form.data.getlist('sites')] |
57
|
|
|
def intersects_sites(sibling): |
58
|
|
|
return sibling.sites.filter(id__in=site_ids).count() > 0 |
59
|
|
|
else: |
60
|
|
|
def intersects_sites(sibling): |
61
|
|
|
return True |
62
|
|
|
return intersects_sites |
63
|
|
|
|
64
|
|
|
def make_form(model_, placeholders): |
65
|
|
|
|
66
|
|
|
# a new form is needed every single time as some |
67
|
|
|
# initial data are bound |
68
|
|
|
class PageForm(forms.ModelForm): |
69
|
|
|
"""Form for page creation""" |
70
|
|
|
|
71
|
|
|
def __init__(self, *args, **kwargs): |
72
|
|
|
super(PageForm, self).__init__(*args, **kwargs) |
73
|
|
|
for p in placeholders: |
74
|
|
|
if not self.fields[p.ctype]: |
75
|
|
|
self.fields[p.ctype] = forms.TextField() |
76
|
|
|
|
77
|
|
|
target = forms.IntegerField(required=False, widget=forms.HiddenInput) |
78
|
|
|
position = forms.CharField(required=False, widget=forms.HiddenInput) |
79
|
|
|
|
80
|
|
|
class Meta: |
81
|
|
|
model = model_ |
82
|
|
|
exclude = ('author', 'last_modification_date', 'parent') |
83
|
|
|
|
84
|
|
|
title = forms.CharField( |
85
|
|
|
label=_('Title'), |
86
|
|
|
widget=forms.TextInput(), |
87
|
|
|
) |
88
|
|
|
slug = forms.CharField( |
89
|
|
|
label=_('Slug'), |
90
|
|
|
widget=forms.TextInput(), |
91
|
|
|
help_text=_('The slug will be used to create the page URL, it must be unique among the other pages of the same level.') |
92
|
|
|
) |
93
|
|
|
|
94
|
|
|
language = forms.ChoiceField( |
95
|
|
|
label=_('Language'), |
96
|
|
|
choices=settings.PAGE_LANGUAGES, |
97
|
|
|
widget=LanguageChoiceWidget() |
98
|
|
|
) |
99
|
|
|
template = forms.ChoiceField( |
100
|
|
|
required=False, |
101
|
|
|
label=_('Template'), |
102
|
|
|
choices=settings.get_page_templates(), |
103
|
|
|
) |
104
|
|
|
delegate_to = forms.ChoiceField( |
105
|
|
|
required=False, |
106
|
|
|
label=_('Delegate to application'), |
107
|
|
|
choices=get_choices(), |
108
|
|
|
) |
109
|
|
|
freeze_date = forms.DateTimeField( |
110
|
|
|
required=False, |
111
|
|
|
label=_('Freeze'), |
112
|
|
|
help_text=_("Don't publish any content after this date. Format is 'Y-m-d H:M:S'") |
113
|
|
|
# those make tests fail miserably |
114
|
|
|
#widget=widgets.AdminSplitDateTime() |
115
|
|
|
#widget=widgets.AdminTimeWidget() |
116
|
|
|
) |
117
|
|
|
|
118
|
|
|
def clean_slug(self): |
119
|
|
|
"""Handle move action on the pages""" |
120
|
|
|
|
121
|
|
|
slug = slugify(self.cleaned_data['slug']) |
122
|
|
|
target = self.data.get('target', None) |
123
|
|
|
position = self.data.get('position', None) |
124
|
|
|
|
125
|
|
|
# this enforce a unique slug for every page |
126
|
|
|
if settings.PAGE_AUTOMATIC_SLUG_RENAMING: |
127
|
|
|
def is_slug_safe(slug): |
128
|
|
|
content = Content.objects.get_content_slug_by_slug(slug) |
129
|
|
|
if content is None: |
130
|
|
|
return True |
131
|
|
|
if self.instance.id: |
132
|
|
|
if content.page.id == self.instance.id: |
133
|
|
|
return True |
134
|
|
|
else: |
135
|
|
|
return False |
136
|
|
|
|
137
|
|
|
return automatic_slug_renaming(slug, is_slug_safe) |
138
|
|
|
|
139
|
|
|
if settings.PAGE_UNIQUE_SLUG_REQUIRED: |
140
|
|
|
# We can return here as not futher checks |
141
|
|
|
# are necessary |
142
|
|
|
return unique_slug_required(self, slug) |
143
|
|
|
|
144
|
|
|
intersects_sites = intersect_sites_method(self) |
145
|
|
|
|
146
|
|
|
if not settings.PAGE_UNIQUE_SLUG_REQUIRED: |
147
|
|
|
if target and position: |
148
|
|
|
target = Page.objects.get(pk=target) |
149
|
|
|
if position in ['right', 'left']: |
150
|
|
|
slugs = [sibling.slug() for sibling in |
151
|
|
|
target.get_siblings() |
152
|
|
|
if intersects_sites(sibling)] |
153
|
|
|
slugs.append(target.slug()) |
154
|
|
|
if slug in slugs: |
155
|
|
|
raise forms.ValidationError(error_dict['sibling_position_error']) |
156
|
|
|
if position == 'first-child': |
157
|
|
|
if slug in [sibling.slug() for sibling in |
158
|
|
|
target.get_children() |
159
|
|
|
if intersects_sites(sibling)]: |
160
|
|
|
raise forms.ValidationError(error_dict['child_error']) |
161
|
|
|
else: |
162
|
|
|
if self.instance.id: |
163
|
|
|
if (slug in [sibling.slug() for sibling in |
164
|
|
|
self.instance.get_siblings().exclude( |
165
|
|
|
id=self.instance.id |
166
|
|
|
) if intersects_sites(sibling)]): |
167
|
|
|
raise forms.ValidationError(error_dict['sibling_error']) |
168
|
|
|
else: |
169
|
|
|
if slug in [sibling.slug() for sibling in |
170
|
|
|
Page.objects.root() |
171
|
|
|
if intersects_sites(sibling)]: |
172
|
|
|
raise forms.ValidationError(error_dict['sibling_root_error']) |
173
|
|
|
return slug |
174
|
|
|
|
175
|
|
|
return PageForm |
176
|
|
|
|