forked from django-cms/django-cms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
page_resolver.py
191 lines (163 loc) · 7.12 KB
/
page_resolver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# -*- coding: utf-8 -*-
from cms.utils.moderator import use_draft
import re
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from cms.utils.compat.dj import force_unicode
from cms.utils.compat.urls import unquote
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _, ungettext_lazy
from cms.exceptions import NoHomeFound
from cms.models.pagemodel import Page
from cms.utils.urlutils import any_path_re
ADMIN_PAGE_RE_PATTERN = r'cms/page/(\d+)'
ADMIN_PAGE_RE = re.compile(ADMIN_PAGE_RE_PATTERN)
def get_page_queryset(request=None):
if request and use_draft(request):
return Page.objects.drafts()
return Page.objects.public()
def get_page_queryset_from_path(path, preview=False, draft=False, site=None):
""" Returns a queryset of pages corresponding to the path given
In may returns None or a single page is no page is present or root path is given
"""
if 'django.contrib.admin' in settings.INSTALLED_APPS:
admin_base = reverse('admin:index')
else:
admin_base = None
# Check if this is called from an admin request
if admin_base and path.startswith(admin_base):
# if so, get the page ID to request it directly
match = ADMIN_PAGE_RE.search(path)
if not match:
page = None
else:
try:
page = Page.objects.get(pk=match.group(1))
except Page.DoesNotExist:
page = None
return page
if not site:
site = Site.objects.get_current()
# PageQuerySet.published filter on page site.
# We have to explicitly filter on site only in preview mode
if draft:
pages = Page.objects.drafts().filter(site=site)
elif preview:
pages = Page.objects.public().filter(site=site)
else:
pages = Page.objects.public().published(site)
# Check if there are any pages
if not pages.all_root().exists():
return None
# get the home page (needed to get the page)
try:
home = pages.get_home(site=site)
except NoHomeFound:
home = None
# if there is no path (slashes stripped) and we found a home, this is the
# home page.
if not path and home:
page = home
return page
# title_set__path=path should be clear, get the pages where the path of the
# title object is equal to our path.
return pages.filter(title_set__path=path).distinct()
def get_page_from_path(path, preview=False, draft=False):
""" Resolves a url path to a single page object.
Raises exceptions is page does not exist or multiple pages are found
"""
page_qs = get_page_queryset_from_path(path, preview, draft)
if page_qs is not None:
if isinstance(page_qs, Page):
return page_qs
try:
page = page_qs.get()
except Page.DoesNotExist:
return None
return page
else:
return None
def get_page_from_request(request, use_path=None):
"""
Gets the current page from a request object.
URLs can be of the following form (this should help understand the code):
http://server.whatever.com/<some_path>/"pages-root"/some/page/slug
<some_path>: This can be anything, and should be stripped when resolving
pages names. This means the CMS is not installed at the root of the
server's URLs.
"pages-root" This is the root of Django urls for the CMS. It is, in essence
an empty page slug (slug == '')
The page slug can then be resolved to a Page model object
"""
# The following is used by cms.middleware.page.CurrentPageMiddleware
if hasattr(request, '_current_page_cache'):
return request._current_page_cache
draft = use_draft(request)
preview = 'preview' in request.GET
# If use_path is given, someone already did the path cleaning
if use_path is not None:
path = use_path
else:
path = request.path
pages_root = unquote(reverse("pages-root"))
# otherwise strip off the non-cms part of the URL
if 'django.contrib.admin' in settings.INSTALLED_APPS:
admin_base = reverse('admin:index')
else:
admin_base = None
if path.startswith(pages_root) and (not admin_base or not path.startswith(admin_base)):
path = path[len(pages_root):]
# and strip any final slash
if path.endswith("/"):
path = path[:-1]
page = get_page_from_path(path, preview, draft)
if draft and page and not page.has_change_permission(request):
page = get_page_from_path(path, preview, draft=False)
request._current_page_cache = page
return page
def is_valid_url(url, instance, create_links=True, site=None):
""" Checks for conflicting urls
"""
page_root = unquote(reverse("pages-root"))
if url and url != page_root:
# Url sanity check via regexp
if not any_path_re.match(url):
raise ValidationError(_('Invalid URL, use /my/url format.'))
# We only check page FK to site object to allow is_valid_url check on
# incomplete Page instances
if not site and instance.site_id:
site = instance.site
# Retrieve complete queryset of pages with corresponding URL
# This uses the same resolving function as ``get_page_from_path``
if url.startswith(page_root):
url = url[len(page_root):]
page_qs = get_page_queryset_from_path(url.strip('/'), site=site)
url_clashes = []
# If queryset has pages checks for conflicting urls
if page_qs is not None:
# If single page is returned create a list for interface compat
if isinstance(page_qs, Page):
page_qs = [page_qs]
for page in page_qs:
# Every page in the queryset except the current one is a conflicting page
# We have to exclude both copies of the page
if page and page.publisher_public.pk != instance.pk:
if create_links:
# Format return message with page url
url_clashes.append('<a href="%(page_url)s%(pk)s" target="_blank">%(page_title)s</a>' % {
'page_url': reverse('admin:cms_page_changelist'), 'pk': page.pk,
'page_title': force_unicode(page),
})
else:
# Just return the page name
url_clashes.append("'%s'" % page)
if url_clashes:
# If clashing pages exist raise the exception
raise ValidationError(mark_safe(
ungettext_lazy('Page %(pages)s has the same url \'%(url)s\' as current page "%(instance)s".',
'Pages %(pages)s have the same url \'%(url)s\' as current page "%(instance)s".',
len(url_clashes)) %
{'pages': ', '.join(url_clashes), 'url': url, 'instance': instance}))
return True