Skip to content

Commit 4ad7c32

Browse files
committed
Merge remote-tracking branch 'upstream/support/3.0.x' into c
Conflicts: CHANGELOG.txt cms/admin/change_list.py cms/cms_toolbar.py cms/menu.py cms/templatetags/cms_tags.py cms/test_utils/project/sampleapp/menu.py cms/test_utils/project/sampleapp/urls.py cms/test_utils/project/sampleapp/views.py cms/tests/apphooks.py cms/tests/menu.py cms/tests/multilingual.py cms/tests/placeholder.py cms/tests/toolbar.py cms/utils/plugins.py menus/menu_pool.py menus/templatetags/menu_tags.py setup.py test_requirements/django-1.4.txt test_requirements/django-1.5.txt test_requirements/requirements_base.txt
2 parents 65281b4 + 8bdef8d commit 4ad7c32

30 files changed

+427
-105
lines changed

CHANGELOG.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,11 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
361361
- Limits version of django-mptt used in CMS for 3.0.x
362362
- Prevent accidental upgrades to Django 1.8, which is not yet supported
363363

364-
==== 3.1 (unreleased) ===
364+
=== 3.0.14 (XXXX-XX-XX) ===
365+
366+
- Fixed an issue related to "Empty all" Placeholder feature
367+
368+
=== 3.1 (2015-04-20) ===
365369

366370
- Remove django-mptt in favor of django-treebeard
367371
- Remove compatibility with Django 1.4 / 1.5
@@ -374,4 +378,8 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
374378
- Remove deprecated cms.context_processors.media context processor
375379
- Add templatetag render_plugin_block
376380
- Add templatetag render_model_add_block
377-
- Add "Structure mode" permission
381+
- Add "Structure mode" permission
382+
383+
=== 3.1.1 (XXXX-XX-XX) ===
384+
385+
- Add Django 1.8 support

cms/admin/change_list.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ def get_results(self, request):
9090
if self.real_queryset:
9191
super(CMSChangeList, self).get_results(request)
9292
if not self.is_filtered():
93-
self.full_result_count = self.result_count = self.root_queryset.count()
93+
self.full_result_count = self.result_count = self.root_queryset.drafts().count()
9494
else:
95-
self.full_result_count = self.root_queryset.count()
95+
self.full_result_count = self.root_queryset.drafts().count()
9696

9797
def set_items(self, request):
9898
site = self.current_site()

cms/admin/forms.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ def __init__(self, *args, **kwargs):
235235
self.fields['language'].initial = get_language()
236236
if 'navigation_extenders' in self.fields:
237237
self.fields['navigation_extenders'].widget = forms.Select(
238-
{}, [('', "---------")] + menu_pool.get_menus_by_attribute("cms_enabled", True))
238+
{}, [('', "---------")] + menu_pool.get_menus_by_attribute(
239+
"cms_enabled", True))
239240
if 'application_urls' in self.fields:
240241
# Prepare a dict mapping the apps by class name ('PollApp') to
241242
# their app_name attribute ('polls'), if any.

cms/appresolver.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import with_statement
33

4+
try:
5+
from collections import OrderedDict
6+
except ImportError:
7+
# Python < 2.7
8+
from django.utils.datastructures import SortedDict as OrderedDict
9+
410
from django.conf import settings
511
from django.contrib.sites.models import Site
612
from django.core.exceptions import ImproperlyConfigured
@@ -198,10 +204,11 @@ def get_app_patterns():
198204

199205
title_qs = Title.objects.public().filter(page__site=current_site)
200206

201-
hooked_applications = {}
207+
hooked_applications = OrderedDict()
202208

203209
# Loop over all titles with an application hooked to them
204-
for title in title_qs.exclude(page__application_urls=None).exclude(page__application_urls='').select_related():
210+
# TODO: Need to be fixed for django-treebeard when forward ported to 3.1
211+
for title in title_qs.exclude(page__application_urls=None).exclude(page__application_urls='').order_by('-page__path').select_related():
205212
path = title.path
206213
mix_id = "%s:%s:%s" % (path + "/", title.page.application_urls, title.language)
207214
if mix_id in included:

cms/cms_toolbar.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from cms.toolbar.items import TemplateItem
1717
from cms.toolbar_base import CMSToolbar
1818
from cms.toolbar_pool import toolbar_pool
19-
from cms.utils.i18n import get_language_tuple, force_language
19+
from cms.utils.i18n import get_language_tuple, force_language, get_language_dict
2020
from cms.utils.compat.dj import is_installed
2121
from cms.utils import get_cms_setting
2222
from cms.utils.permissions import get_user_sites_queryset
@@ -368,13 +368,11 @@ def change_language_menu(self):
368368
if not language_menu:
369369
return None
370370

371-
languages = get_language_tuple(self.current_site.pk)
372-
languages_dict = dict(languages)
373-
374-
remove = [(code, languages_dict.get(code, code)) for code in self.page.get_languages()]
375-
add = [l for l in languages if l not in remove]
376-
copy = [(code, name) for code, name in languages if code != self.current_lang and (code, name) in remove]
371+
languages = get_language_dict(self.current_site.pk)
377372

373+
remove = [(code, languages.get(code, code)) for code in self.page.get_languages() if code in languages]
374+
add = [l for l in languages.items() if l not in remove]
375+
copy = [(code, name) for code, name in languages.items() if code != self.current_lang and (code, name) in remove]
378376
if add:
379377
language_menu.add_break(ADD_PAGE_LANGUAGE_BREAK)
380378
page_change_url = admin_reverse('cms_page_change', args=(self.page.pk,))

cms/menu.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,11 @@ def page_to_node(page, home, cut):
128128
# Extenders can be either navigation extenders or from apphooks.
129129
extenders = []
130130
if page.navigation_extenders:
131-
extenders.append(page.navigation_extenders)
132-
# Is this page an apphook? If so, we need to handle the apphooks's nodes
131+
if page.navigation_extenders in menu_pool.menus:
132+
extenders.append(page.navigation_extenders)
133+
elif "{0}:{1}".format(page.navigation_extenders, page.pk) in menu_pool.menus:
134+
extenders.append("{0}:{1}".format(page.navigation_extenders, page.pk))
135+
# Is this page an apphook? If so, we need to handle the apphooks's nodes
133136
lang = get_language()
134137
# Only run this if we have a translation in the requested language for this
135138
# object. The title cache should have been prepopulated in CMSMenu.get_nodes
@@ -201,9 +204,11 @@ def get_nodes(self, request):
201204
home = page
202205
if first and page.pk != home.pk:
203206
home_cut = True
204-
if (page.parent_id == home.pk or page.parent_id in home_children) and home_cut:
207+
if (home_cut and (page.parent_id == home.pk or
208+
page.parent_id in home_children)):
205209
home_children.append(page.pk)
206-
if (page.pk == home.pk and home.in_navigation) or page.pk != home.pk:
210+
if ((page.pk == home.pk and home.in_navigation)
211+
or page.pk != home.pk):
207212
first = False
208213
ids[page.id] = page
209214
actual_pages.append(page)
@@ -213,7 +218,8 @@ def get_nodes(self, request):
213218
if not hide_untranslated(lang):
214219
langs.extend(get_fallback_languages(lang))
215220

216-
titles = list(get_title_queryset(request).filter(page__in=ids, language__in=langs))
221+
titles = list(get_title_queryset(request).filter(
222+
page__in=ids, language__in=langs))
217223
for title in titles: # add the title and slugs and some meta data
218224
page = ids[title.page_id]
219225
page.title_cache[title.language] = title
@@ -231,23 +237,23 @@ class NavExtender(Modifier):
231237
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
232238
if post_cut:
233239
return nodes
234-
exts = []
235240
# rearrange the parent relations
236-
home = None
241+
# Find home
242+
home = next((n for n in nodes if n.attr.get("is_home", False)), None)
243+
# Find nodes with NavExtenders
244+
exts = []
237245
for node in nodes:
238-
if node.attr.get("is_home", False):
239-
home = node
240246
extenders = node.attr.get("navigation_extenders", None)
241247
if extenders:
242248
for ext in extenders:
243249
if ext not in exts:
244250
exts.append(ext)
251+
# Link the nodes
245252
for extnode in nodes:
246253
if extnode.namespace == ext and not extnode.parent_id:
247254
# if home has nav extenders but home is not visible
248-
if (node.attr.get("is_home", False)
249-
and not node.visible):
250-
extnode.parent_id = None
255+
if node == home and not node.visible:
256+
# extnode.parent_id = None
251257
extnode.parent_namespace = None
252258
extnode.parent = None
253259
else:
@@ -398,7 +404,8 @@ def find_ancestors_and_remove_children(self, node, nodes):
398404
node.parent.parent = None
399405
nodes = [node.parent] + nodes
400406
else:
401-
nodes = self.find_ancestors_and_remove_children(node.parent, nodes)
407+
nodes = self.find_ancestors_and_remove_children(
408+
node.parent, nodes)
402409
else:
403410
for newnode in nodes:
404411
if newnode != node and not newnode.parent:

cms/static/cms/js/modules/cms.changelist.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ $(document).ready(function () {
2929
initTree();
3030
} else {
3131
// when filtered is active, prevent tree actions
32+
this.setupFunctions();
3233
this.setupUIHacks();
34+
$.syncCols();
3335
}
3436
},
3537

cms/templatetags/cms_tags.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
from itertools import chain
55
import re
66

7+
try:
8+
from collections import OrderedDict
9+
except ImportError:
10+
from django.utils.datastructures import SortedDict as OrderedDict
11+
712
from django import template
813
from django.conf import settings
914
from django.contrib.sites.models import Site
@@ -910,8 +915,9 @@ def _get_empty_context(self, context, instance, edit_fields, language,
910915
edit_fields = 'title,page_title,menu_title'
911916
view_url = 'admin:cms_page_edit_title_fields'
912917
if edit_fields == 'changelist':
913-
view_url = 'admin:cms_page_changelist'
914-
querystring = {'language': language}
918+
view_url = 'admin:%s_%s_changelist' % (
919+
instance._meta.app_label, instance._meta.model_name)
920+
querystring = OrderedDict((('language', language),))
915921
if edit_fields:
916922
extra_context['edit_fields'] = edit_fields.strip().split(",")
917923
# If the toolbar is not enabled the following part is just skipped: it

cms/test_utils/project/sampleapp/cms_app.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from cms.app_base import CMSApp
2-
from cms.test_utils.project.sampleapp.menu import SampleAppMenu
2+
from cms.test_utils.project.sampleapp.menu import SampleAppMenu, StaticMenu3
33
from cms.apphook_pool import apphook_pool
44
from django.utils.translation import ugettext_lazy as _
55

@@ -27,6 +27,7 @@ class SampleAppWithExcludedPermissions(CMSApp):
2727
class SampleApp2(CMSApp):
2828
name = _("Sample App 2")
2929
urls = ["cms.test_utils.project.sampleapp.urls2"]
30+
menus = [StaticMenu3]
3031

3132
apphook_pool.register(SampleApp2)
3233

@@ -37,7 +38,21 @@ class NamespacedApp(CMSApp):
3738
"cms.test_utils.project.sampleapp.ns_urls",
3839
"cms.test_utils.project.sampleapp.urls"
3940
]
40-
menus = [SampleAppMenu]
41+
menus = [SampleAppMenu, StaticMenu3]
4142
app_name = 'namespaced_app_ns'
4243

4344
apphook_pool.register(NamespacedApp)
45+
46+
47+
class ParentApp(CMSApp):
48+
name = _("Parent app")
49+
urls = ["cms.test_utils.project.sampleapp.urls_parentapp"]
50+
51+
apphook_pool.register(ParentApp)
52+
53+
54+
class ChildApp(CMSApp):
55+
name = _("Child app")
56+
urls = ["cms.test_utils.project.sampleapp.urls_childapp"]
57+
58+
apphook_pool.register(ChildApp)

cms/test_utils/project/sampleapp/menu.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,10 @@ def get_nodes(self, request):
4747
menu_pool.register_menu(StaticMenu)
4848

4949

50-
class StaticMenu2(CMSAttachMenu):
50+
class StaticMenu2(StaticMenu):
5151
name = _("Static Menu2")
52-
53-
def get_nodes(self, request):
54-
nodes = []
55-
n = NavigationNode('static2 root page', "/fresh/", 1)
56-
n2 = NavigationNode('static2 settings page', "/bye/", 2)
57-
n3 = NavigationNode('static2 account page', "/hello/", 3)
58-
n4 = NavigationNode('static2 my profile page', "/hello/world/", 4, 3)
59-
nodes.append(n)
60-
nodes.append(n2)
61-
nodes.append(n3)
62-
nodes.append(n4)
63-
return nodes
64-
6552
menu_pool.register_menu(StaticMenu2)
53+
54+
class StaticMenu3(StaticMenu):
55+
name = _("Static Menu3")
56+
menu_pool.register_menu(StaticMenu3)

cms/test_utils/project/sampleapp/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
from . import views
55

6+
"""
7+
Also used in cms.tests.ApphooksTestCase
8+
"""
69
urlpatterns = [
710
url(r'^$', views.sample_view, {'message': 'sample root page',}, name='sample-root'),
811
url(r'^exempt/$', views.exempt_view, {'message': 'sample root page',}, name='sample-exempt'),
@@ -13,5 +16,7 @@
1316
url(r'^category/(?P<id>[0-9]+)/$', views.category_view, name='category_view'),
1417
url(r'^notfound/$', views.notfound, name='notfound'),
1518
url(r'^extra_1/$', views.extra_view, {'message': 'test urlconf'}, name='extra_first'),
19+
url(r'^class-view/$', views.ClassView(), name='sample-class-view'),
20+
url(r'^class-based-view/$', views.ClassBasedView.as_view(), name='sample-class-based-view'),
1621
url(r'^', include('cms.test_utils.project.sampleapp.urls_extra'), {'opts': 'someopts'}),
1722
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.conf.urls import url
2+
3+
from cms.test_utils.project.sampleapp import views
4+
5+
urlpatterns = [
6+
url(r'^(?P<path>.+)$', views.childapp_view, name='childapp_view'),
7+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.conf.urls import url
2+
3+
from cms.test_utils.project.sampleapp import views
4+
5+
urlpatterns = [
6+
url(r'^(?P<path>.+)$', views.parentapp_view, name='parentapp_view'),
7+
]

cms/test_utils/project/sampleapp/views.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# Create your views here.
2-
from django.views.decorators.csrf import csrf_exempt
3-
from cms.utils.urlutils import admin_reverse
42
from django.http import Http404
53
from django.shortcuts import render
6-
from cms.test_utils.project.sampleapp.models import Category
74
from django.utils.translation import ugettext_lazy as _
5+
from django.views.decorators.csrf import csrf_exempt
6+
from django.views.generic.base import TemplateView
7+
8+
from cms.test_utils.project.sampleapp.models import Category
9+
from cms.utils.urlutils import admin_reverse
810

911

1012
@csrf_exempt
@@ -54,3 +56,23 @@ def plain_view(request):
5456

5557
def notfound(request):
5658
raise Http404
59+
60+
61+
class ClassView(object):
62+
def __call__(self, request, *args, **kwargs):
63+
context = {'content': 'plain text'}
64+
return render(request, "sampleapp/plain.html", context)
65+
66+
67+
class ClassBasedView(TemplateView):
68+
template_name = 'sampleapp/plain.html'
69+
70+
71+
def parentapp_view(request, path):
72+
context = {'content': 'parent app content'}
73+
return render(request, "sampleapp/plain.html", context)
74+
75+
76+
def childapp_view(request, path):
77+
context = {'content': 'child app content'}
78+
return render(request, "sampleapp/plain.html", context)

0 commit comments

Comments
 (0)