Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'develop' into fix-2685

Conflicts:
	cms/templates/admin/cms/page/tree/menu_item.html
  • Loading branch information...
commit b7137c246261624e06880a979be035c8a8944ce8 2 parents 1f5db0c + ca9bdc7
@digi604 authored
Showing with 718 additions and 213 deletions.
  1. +2 −1  CHANGELOG.txt
  2. +25 −3 cms/admin/pageadmin.py
  3. +2 −0  cms/cms_toolbar.py
  4. +3 −3 cms/context_processors.py
  5. +11 −2 cms/middleware/toolbar.py
  6. +31 −4 cms/models/pagemodel.py
  7. +2 −0  cms/plugin_base.py
  8. +7 −0 cms/signals/apphook.py
  9. +1 −1  cms/static/cms/css/cms.base.css
  10. +36 −1 cms/static/cms/js/plugins/cms.base.js
  11. +45 −41 cms/static/cms/js/plugins/cms.modal.js
  12. +1 −1  cms/static/cms/js/plugins/cms.plugins.js
  13. +7 −0 cms/static/cms/js/plugins/cms.sideframe.js
  14. +2 −2 cms/static/cms/js/plugins/cms.structureboard.js
  15. +16 −17 cms/static/cms/js/plugins/cms.toolbar.js
  16. +3 −0  cms/static/cms/sass/modules/_structureboard.scss
  17. +5 −4 cms/templates/admin/cms/page/permissions.html
  18. +3 −0  cms/templates/admin/cms/page/tree/base.html
  19. +3 −3 cms/templates/admin/cms/page/tree/menu_item.html
  20. +1 −1  cms/templates/cms/toolbar/items/item_modal.html
  21. +1 −1  cms/templates/cms/toolbar/items/item_sideframe.html
  22. +2 −3 cms/templates/cms/toolbar/plugin.html
  23. +0 −1  cms/templates/cms/toolbar/toolbar.html
  24. +5 −0 cms/templates/cms/toolbar/toolbar_javascript.html
  25. +2 −0  cms/templatetags/cms_tags.py
  26. +0 −1  cms/test_utils/cli.py
  27. +1 −1  cms/tests/admin.py
  28. +84 −5 cms/tests/cache.py
  29. +36 −0 cms/tests/i18n.py
  30. +1 −1  cms/tests/multilingual.py
  31. +165 −60 cms/tests/toolbar.py
  32. +25 −2 cms/toolbar/toolbar.py
  33. +1 −0  cms/toolbar_base.py
  34. +93 −7 cms/views.py
  35. +49 −31 develop.py
  36. +4 −16 docs/extending_cms/api_references.rst
  37. +8 −0 docs/extending_cms/custom_plugins.rst
  38. +23 −0 docs/extending_cms/toolbar.rst
  39. +12 −0 docs/upgrade/3.0.rst
View
3  CHANGELOG.txt
@@ -213,4 +213,5 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
- Publishing is now language independent and the tree-view has been updated to reflect this
- Removed the plugin DB-name magic and added a compatibility layer
- urls_need_reloading signal added when an apphook change is detected.
-- CMS_PAGE_CACHE, CMS_PLACEHOLDER_CACHE and CMS_PLUGIN_CACHE settings and functionality added. Default is True
+- CMS_PAGE_CACHE, CMS_PLACEHOLDER_CACHE and CMS_PLUGIN_CACHE settings and functionality added. Default is True
+- Detect admin object creation and changes via toolbar and redirect to them.
View
28 cms/admin/pageadmin.py
@@ -130,7 +130,7 @@ def get_urls(self):
pat(r'^([0-9]+)/([a-z\-]+)/unpublish/$', self.unpublish),
pat(r'^([0-9]+)/([a-z\-]+)/revert/$', self.revert_page),
pat(r'^([0-9]+)/([a-z\-]+)/preview/$', self.preview_page),
-
+ url(r'^resolve/$', self.resolve, name="cms_page_resolve"),
)
if plugin_pool.get_all_plugins():
@@ -1161,8 +1161,7 @@ def change_innavigation(self, request, page_id):
"""
page = get_object_or_404(Page, pk=page_id)
if page.has_change_permission(request):
- page.in_navigation = not page.in_navigation
- page.save()
+ page.toggle_in_navigation()
return admin_utils.render_admin_menu_item(request, page)
return HttpResponseForbidden(force_unicode(_("You do not have permission to change this page's in_navigation status")))
@@ -1178,6 +1177,29 @@ def descendants(self, request, page_id):
return admin_utils.render_admin_menu_item(request, page,
template="admin/cms/page/tree/lazy_menu.html")
+ def resolve(self, request):
+ if not request.user.is_staff:
+ return HttpResponse('/')
+ if request.session.get('cms_log_latest', False):
+ log = LogEntry.objects.get(pk=request.session['cms_log_latest'])
+ obj = log.get_edited_object()
+ del request.session['cms_log_latest']
+ try:
+ return HttpResponse(force_unicode(obj.get_absolute_url()))
+ except:
+ pass
+ pk = request.REQUEST.get('pk')
+ app_label, model = request.REQUEST.get('model').split('.')
+ if pk and app_label:
+ ctype = ContentType.objects.get(app_label=app_label, model=model)
+ try:
+ instance = ctype.get_object_for_this_type(pk=pk)
+ except ctype.model_class().DoesNotExist:
+ return HttpResponse('/')
+ return HttpResponse(force_unicode(instance.get_absolute_url()))
+ else:
+ return HttpResponse('/')
+
def lookup_allowed(self, key, *args, **kwargs):
if key == 'site__exact':
return True
View
2  cms/cms_toolbar.py
@@ -120,6 +120,8 @@ def add_language_menu(self):
@toolbar_pool.register
class PageToolbar(CMSToolbar):
+ model = Title
+
def populate(self):
# always use draft if we have a page
self.page = get_page_draft(self.request.current_page)
View
6 cms/context_processors.py
@@ -7,7 +7,7 @@ def cms_settings(request):
"""
Adds cms-related variables to the context.
"""
-
+
return {
'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
'CMS_TEMPLATE': lambda: get_template_from_request(request),
@@ -15,7 +15,7 @@ def cms_settings(request):
def media(request):
- warnings.warn('cms.context_processors.media has been deprecated in favor of'
- 'cms.context_processors.cms_settings. Please update your'
+ warnings.warn('cms.context_processors.media has been deprecated in favor of '
+ 'cms.context_processors.cms_settings. Please update your '
'configuration', DeprecationWarning)
return cms_settings(request)
View
13 cms/middleware/toolbar.py
@@ -5,6 +5,7 @@
from cms.plugin_pool import plugin_pool
from cms.toolbar.toolbar import CMSToolbar
from cms.utils.i18n import force_language
+from django.contrib.admin.models import LogEntry
from menus.menu_pool import menu_pool
from django.http import HttpResponse
from django.template.loader import render_to_string
@@ -64,13 +65,14 @@ def process_request(self, request):
request.session['cms_build'] = False
if 'build' in request.GET and not request.session.get('cms_build', False):
request.session['cms_build'] = True
+ if request.user.is_staff:
+ request.session['cms_log_entries'] = LogEntry.objects.filter(user=request.user).count()
request.toolbar = CMSToolbar(request)
def process_view(self, request, view_func, view_args, view_kwarg):
response = request.toolbar.request_hook()
if isinstance(response, HttpResponse):
return response
- return None
def process_response(self, request, response):
from django.utils.cache import add_never_cache_headers
@@ -83,4 +85,11 @@ def process_response(self, request, response):
break
if found:
add_never_cache_headers(response)
- return response
+ if request.user.is_staff:
+ count = LogEntry.objects.filter(user=request.user).count()
+ if request.session.get('cms_log_entries', 0) < count:
+ request.session['cms_log_entries'] = count
+ log = LogEntry.objects.filter(user=request.user)[0]
+ if log.action_flag == 1 or log.action_flag == 2:
+ request.session['cms_log_latest'] = log.pk
+ return response
View
35 cms/models/pagemodel.py
@@ -4,7 +4,7 @@
from os.path import join
from cms import constants
from cms.constants import PUBLISHER_STATE_DEFAULT, PUBLISHER_STATE_PENDING, PUBLISHER_STATE_DIRTY, TEMPLATE_INHERITANCE_MAGIC
-from cms.exceptions import PublicIsUnmodifiable, LanguageError
+from cms.exceptions import PublicIsUnmodifiable, LanguageError, PublicVersionNeeded
from cms.models.managers import PageManager, PagePermissionsPermissionManager
from cms.models.metaclasses import PageMetaClass
from cms.models.placeholdermodel import Placeholder
@@ -193,6 +193,9 @@ def move_page(self, target, position='first-child'):
public_page.save()
page_utils.check_title_slugs(public_page)
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
+
def _copy_titles(self, target, language, published):
"""
Copy all the titles to a new page (which must have a pk).
@@ -318,6 +321,7 @@ def copy_page(self, target, site, position='first-child',
page.lft = None
page.tree_id = None
page.publisher_public_id = None
+ page.is_home = False
# only set reverse_id on standard copy
if page.reverse_id in site_reverse_ids:
page.reverse_id = None
@@ -477,6 +481,19 @@ def is_published(self, language, force_reload=False):
except Title.DoesNotExist:
return False
+ def toggle_in_navigation(self, set_to=None):
+ '''
+ Toggles (or sets) in_navigation and invalidates the cms page cache
+ '''
+ if set_to in [True, False]:
+ self.in_navigation = set_to
+ else:
+ self.in_navigation = not self.in_navigation
+ self.save()
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
+ return self.in_navigation
+
def get_publisher_state(self, language, force_reload=False):
from cms.models import Title
@@ -579,10 +596,12 @@ def publish(self, language):
if page.publisher_public:
if page.publisher_public.parent.is_published(language):
from cms.models import Title
-
- public_title = Title.objects.get(page=page.publisher_public, language=language)
+ try:
+ public_title = Title.objects.get(page=page.publisher_public, language=language)
+ except Title.DoesNotExist:
+ public_title = None
draft_title = Title.objects.get(page=page, language=language)
- if not public_title.published:
+ if public_title and not public_title.published:
public_title._publisher_keep_state = True
public_title.published = True
public_title.publisher_state = PUBLISHER_STATE_DEFAULT
@@ -598,6 +617,9 @@ def publish(self, language):
cms_signals.post_publish.send(sender=Page, instance=self, language=language)
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
+
return published
def unpublish(self, language):
@@ -628,8 +650,13 @@ def unpublish(self, language):
# trigger update home
self.save()
self.mark_descendants_pending(language)
+
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
+
from cms.signals import post_unpublish
post_unpublish.send(sender=Page, instance=self, language=language)
+
return True
def mark_descendants_pending(self, language):
View
2  cms/plugin_base.py
@@ -114,6 +114,8 @@ class CMSPluginBase(with_metaclass(CMSPluginBaseMetaclass, admin.ModelAdmin)):
require_parent = False
parent_classes = None
+ disable_child_plugin = False
+
cache = get_cms_setting('PLUGIN_CACHE')
opts = {}
View
7 cms/signals/apphook.py
@@ -26,6 +26,9 @@ def apphook_post_page_checker(page):
if (old_page and (
old_page.application_urls != page.application_urls or old_page.application_namespace != page.application_namespace)) or (
not old_page and page.application_urls):
+
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
request_finished.connect(trigger_restart, dispatch_uid=DISPATCH_UID)
@@ -53,6 +56,8 @@ def apphook_post_delete_title_checker(instance, **kwargs):
Check if this was an apphook
"""
if instance.page.application_urls:
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
request_finished.connect(trigger_restart, dispatch_uid=DISPATCH_UID)
@@ -61,6 +66,8 @@ def apphook_post_delete_page_checker(instance, **kwargs):
Check if this was an apphook
"""
if instance.application_urls:
+ from cms.views import invalidate_cms_page_cache
+ invalidate_cms_page_cache()
request_finished.connect(trigger_restart, dispatch_uid=DISPATCH_UID)
# import the logging library
View
2  cms/static/cms/css/cms.base.css
@@ -14,6 +14,6 @@
* @copyright: https://github.com/divio/django-cms
*/#cms_toolbar .cms_clipboard{position:fixed;left:0;top:130px;z-index:99999;display:none;background:rgba(255,255,255,0.6);padding:3px 3px 3px 0;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;-ms-border-radius:0 3px 3px 0;-o-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}#cms_toolbar .cms_clipboard p{padding:0;margin:0}#cms_toolbar .cms_clipboard .cms_clipboard-numbers{margin:0 0 2px}#cms_toolbar .cms_clipboard .cms_clipboard-numbers a{display:block;width:20px;height:20px;text-indent:-119988px;overflow:hidden;text-align:left;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;-ms-border-radius:0 3px 3px 0;-o-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;border:5px solid #454545;border-left:none;border-right:none;overflow:hidden;background:#454545 url("../img/toolbar/sprite_toolbar.png") no-repeat left -130px}#cms_toolbar .cms_clipboard .cms_clipboard-numbers a:hover,#cms_toolbar .cms_clipboard .cms_clipboard-numbers a:active,#cms_toolbar .cms_clipboard .cms_clipboard-numbers a:focus{background-color:#333;background-position:-20px -130px}#cms_toolbar .cms_clipboard .cms_clipboard-numbers .cms_clipboard-numbers{display:none !important}#cms_toolbar .cms_clipboard .cms_clipboard-empty a{display:block;width:20px;height:20px;text-indent:-119988px;overflow:hidden;text-align:left;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;-ms-border-radius:0 3px 3px 0;-o-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;background:#454545 url("../img/toolbar/sprite_toolbar.png") no-repeat left -110px;cursor:pointer}#cms_toolbar .cms_clipboard .cms_clipboard-empty a:hover,#cms_toolbar .cms_clipboard .cms_clipboard-empty a:active,#cms_toolbar .cms_clipboard .cms_clipboard-empty a:focus{background-color:#333;background-position:-20px -110px}#cms_toolbar .cms_clipboard .cms_clipboard-triggers{position:relative;z-index:999}#cms_toolbar .cms_clipboard .cms_clipboard-triggers .cms_clipboard-numbers:nth-child(n+6){display:none}#cms_toolbar .cms_clipboard .cms_clipboard-containers{position:absolute;top:0;left:-1px;z-index:99;width:0}#cms_toolbar .cms_clipboard .cms_clipboard-containers .cms_draggable{margin-left:-220px;width:190px}#cms_toolbar .cms_clipboard .cms_dragarea{padding-top:3px}#cms_toolbar .cms_clipboard .cms_draggable{position:relative;left:0;top:0;z-index:99;cursor:move;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:4px 5px 3px 5px;margin:0 0 2px;background:#fafafa;border:1px solid #454545}#cms_toolbar .cms_clipboard .cms_draggable .cms_dragitem{padding-left:20px}#cms_toolbar .cms_clipboard .cms_draggable .cms_dragitem .cms_submenu{display:none !important}#cms_toolbar .cms_clipboard .cms_draggable .cms_dragitem-text{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;width:140px;height:21px;overflow:hidden}#cms_toolbar .cms_clipboard .cms_plugins{display:none}/*!
* @copyright: https://github.com/divio/django-cms
- */#cms_toolbar .cms_structure{display:none;position:absolute;top:0;right:0;width:100%;height:100%;z-index:9999}#cms_toolbar .cms_structure .cms_structure-dimmer{display:none;position:fixed;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:10;background:rgba(255,255,255,0.95)}#cms_toolbar .cms_structure .cms_structure-content{position:absolute;left:0;top:0;z-index:100;width:100%;height:100%}#cms_toolbar .cms_structure .cms_dragarea{position:absolute;padding:5px 5px 4px;margin:0 0 5px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;background:#454545;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#cms_toolbar .cms_structure .cms_dragarea-static{background:#454545 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAGElEQVQIW2NkgID/QMzICGOAREAcGPgPADZsAwNt4b8uAAAAAElFTkSuQmCC)}#cms_toolbar .cms_structure .cms_dragbar{font-size:13px;line-height:20px;position:relative;left:0;top:0;z-index:9999;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title{font-size:12px;line-height:16px;text-transform:uppercase;font-weight:500;padding:0 0 0 15px;height:16px;cursor:pointer;color:white;text-shadow:0px 1px 0px #000}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title:before{content:" ";position:absolute;left:0;top:0;width:16px;height:15px;background:url("../img/toolbar/sprite_toolbar.png") no-repeat -85px -113px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title:hover:before{background-position:-105px -113px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title-expanded:before{background-position:-124px -114px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title-expanded:hover:before{background-position:-144px -114px !important}#cms_toolbar .cms_structure .cms_dragbar-empty{font-size:11px;text-transform:uppercase;padding-top:0;padding-bottom:0}#cms_toolbar .cms_structure .cms_dragbar-empty-wrapper{display:none}#cms_toolbar .cms_structure .cms_draggables{list-style-type:none;padding:0;margin:0}#cms_toolbar .cms_structure .cms_draggables .cms_draggables{display:none;min-height:25px;padding-left:6px}#cms_toolbar .cms_structure .cms_draggables .cms_draggables>.cms_draggable:first-child,#cms_toolbar .cms_structure .cms_draggables .cms_draggables>.cms_draggable:only-child,#cms_toolbar .cms_structure .cms_draggable>.cms_draggable{margin-top:0}#cms_toolbar .cms_structure .cms_draggables>.cms_draggable:last-child{margin-bottom:1px}#cms_toolbar .cms_structure .cms_draggables .cms_draggables>.cms_draggable:last-child{margin-bottom:2px}#cms_toolbar .cms_structure .cms_draggable,#cms_toolbar .cms_structure .cms_droppable{list-style-type:none;position:relative;left:0;top:0;z-index:99;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:4px 5px 3px 5px;margin:5px 0 0;margin-left:0 !important}#cms_toolbar .cms_structure .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable{position:relative;z-index:99;white-space:nowrap;border-color:#e6e6e6;background:white}#cms_toolbar .cms_structure .cms_draggable .cms_draggable:hover,#cms_toolbar .cms_structure .cms_droppable .cms_draggable:hover{border-color:#a6a6a6}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable{background:#fafafa}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable{background:white}#cms_toolbar .cms_structure .cms_draggable .cms_submenu,#cms_toolbar .cms_structure .cms_droppable .cms_submenu{display:none;margin-top:2px}#cms_toolbar .cms_structure .cms_draggable .cms_submenu-dropdown,#cms_toolbar .cms_structure .cms_droppable .cms_submenu-dropdown{right:-6px;top:22px}#cms_toolbar .cms_structure .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_submenu-quicksearch{right:-5px;top:-6px;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;height:28px;border-left:1px dotted #e6e6e6;background:#fafafa url("../img/toolbar/sprite_toolbar.png") no-repeat right -415px}#cms_toolbar .cms_structure .cms_draggable .cms_submenu-quicksearch input,#cms_toolbar .cms_structure .cms_droppable .cms_submenu-quicksearch input{color:black;margin-top:1px}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch{background-color:white}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch{background-color:#fafafa}#cms_toolbar .cms_structure .cms_draggable{z-index:100;color:black;border:1px solid #fafafa;background:#fafafa}#cms_toolbar .cms_structure .cms_draggable:hover{-webkit-box-shadow:inset 0px 0px 3px #e6e6e6;-moz-box-shadow:inset 0px 0px 3px #e6e6e6;box-shadow:inset 0px 0px 3px #e6e6e6}#cms_toolbar .cms_structure .cms_droppable{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;color:#bfbfbf;border:1px dashed #bfbfbf}#cms_toolbar .cms_structure .cms_dragitem{cursor:move}#cms_toolbar .cms_structure .cms_dragitem-collapsable{cursor:pointer;padding-left:15px}#cms_toolbar .cms_structure .cms_dragitem-collapsable{background:url("../img/toolbar/sprite_toolbar.png") no-repeat 1px -358px}#cms_toolbar .cms_structure .cms_dragitem-expanded{background:url("../img/toolbar/sprite_toolbar.png") no-repeat 0 -389px}#cms_toolbar .cms_structure .cms_dragitem-success{position:absolute;left:-1px;top:-1px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;width:100%;height:100%;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=60);opacity:0.6}#cms_toolbar .cms_structure .cms_draggable-selected .cms_dragitem,#cms_toolbar .cms_structure .cms_draggable-selected .cms_dragitem strong{color:#0e72ec}#cms_toolbar .cms_structure .cms_draggable-selected .cms_draggable .cms_dragitem,#cms_toolbar .cms_structure .cms_draggable-selected .cms_draggable .cms_dragitem strong{color:black}#cms_toolbar .cms_structure .cms_draggable-allowed,#cms_toolbar .cms_structure .cms_draggable-hover-allowed,#cms_toolbar .cms_structure .cms_draggable-placeholder{color:#cce6b3;border-color:#cce6b3}#cms_toolbar .cms_structure .cms_draggable-hover-allowed,#cms_toolbar .cms_structure .cms_draggable-placeholder{color:white;background:rgba(102,153,51,0.2)}#cms_toolbar .cms_structure .cms_dragitem-success{border:1px solid #cce6b3;background:#cce6b3}#cms_toolbar .cms_structure .cms_draggable-disallowed,#cms_toolbar .cms_structure .cms_draggable-hover-disallowed{color:red;border:1px dashed red;background:rgba(255,0,0,0.1)}body>.cms_draggable{list-style-type:none;white-space:nowrap;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:4px 5px 3px 5px;margin:0;border-color:#e6e6e6;background:white}body>.cms_draggable .cms_switcher{display:none !important}body>.cms_draggable .cms_submenu{display:none !important}body>.cms_draggable .cms_draggables{display:none !important}/*!
+ */#cms_toolbar .cms_structure{display:none;position:absolute;top:0;right:0;width:100%;height:100%;z-index:9999}#cms_toolbar .cms_structure .cms_structure-dimmer{display:none;position:fixed;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:10;background:rgba(255,255,255,0.95)}#cms_toolbar .cms_structure .cms_structure-content{position:absolute;left:0;top:0;z-index:100;width:100%;height:100%}#cms_toolbar .cms_structure .cms_dragarea{position:absolute;padding:5px 5px 4px;margin:0 0 5px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;background:#454545;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#cms_toolbar .cms_structure .cms_dragarea-static{background:#454545 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAGElEQVQIW2NkgID/QMzICGOAREAcGPgPADZsAwNt4b8uAAAAAElFTkSuQmCC)}#cms_toolbar .cms_structure .cms_dragbar{font-size:13px;line-height:20px;position:relative;left:0;top:0;z-index:9999;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title{font-size:12px;line-height:16px;text-transform:uppercase;font-weight:500;padding:0 0 0 15px;height:16px;cursor:pointer;color:white;text-shadow:0px 1px 0px #000}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title:before{content:" ";position:absolute;left:0;top:0;width:16px;height:15px;background:url("../img/toolbar/sprite_toolbar.png") no-repeat -85px -113px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title:hover:before{background-position:-105px -113px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title-expanded:before{background-position:-124px -114px}#cms_toolbar .cms_structure .cms_dragbar .cms_dragbar-title-expanded:hover:before{background-position:-144px -114px !important}#cms_toolbar .cms_structure .cms_dragbar-empty{font-size:11px;text-transform:uppercase;padding-top:0;padding-bottom:0}#cms_toolbar .cms_structure .cms_dragbar-empty-wrapper{display:none}#cms_toolbar .cms_structure .cms_draggables{list-style-type:none;padding:0;margin:0}#cms_toolbar .cms_structure .cms_draggables .cms_draggables{display:none;min-height:25px;padding-left:6px}#cms_toolbar .cms_structure .cms_draggables .cms_draggables>.cms_draggable:first-child,#cms_toolbar .cms_structure .cms_draggables .cms_draggables>.cms_draggable:only-child,#cms_toolbar .cms_structure .cms_draggable>.cms_draggable{margin-top:0}#cms_toolbar .cms_structure .cms_draggables>.cms_draggable:last-child{margin-bottom:1px}#cms_toolbar .cms_structure .cms_draggables .cms_draggables>.cms_draggable:last-child{margin-bottom:2px}#cms_toolbar .cms_structure .cms_draggable,#cms_toolbar .cms_structure .cms_droppable{list-style-type:none;position:relative;left:0;top:0;z-index:99;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:4px 5px 3px 5px;margin:5px 0 0;margin-left:0 !important}#cms_toolbar .cms_structure .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable{position:relative;z-index:99;white-space:nowrap;border-color:#e6e6e6;background:white}#cms_toolbar .cms_structure .cms_draggable .cms_draggable:hover,#cms_toolbar .cms_structure .cms_droppable .cms_draggable:hover{border-color:#a6a6a6}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable{background:#fafafa}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable{background:white}#cms_toolbar .cms_structure .cms_draggable .cms_submenu,#cms_toolbar .cms_structure .cms_droppable .cms_submenu{display:none;margin-top:2px}#cms_toolbar .cms_structure .cms_draggable .cms_submenu-dropdown,#cms_toolbar .cms_structure .cms_droppable .cms_submenu-dropdown{right:-6px;top:22px}#cms_toolbar .cms_structure .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_submenu-quicksearch{right:-5px;top:-6px;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;height:28px;border-left:1px dotted #e6e6e6;background:#fafafa url("../img/toolbar/sprite_toolbar.png") no-repeat right -415px}#cms_toolbar .cms_structure .cms_draggable .cms_submenu-quicksearch input,#cms_toolbar .cms_structure .cms_droppable .cms_submenu-quicksearch input{color:black;margin-top:1px}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch{background-color:white}#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch,#cms_toolbar .cms_structure .cms_droppable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_draggable .cms_submenu-quicksearch{background-color:#fafafa}#cms_toolbar .cms_structure .cms_draggable{z-index:100;color:black;border:1px solid #fafafa;background:#fafafa}#cms_toolbar .cms_structure .cms_draggable:hover{-webkit-box-shadow:inset 0px 0px 3px #e6e6e6;-moz-box-shadow:inset 0px 0px 3px #e6e6e6;box-shadow:inset 0px 0px 3px #e6e6e6}#cms_toolbar .cms_structure .cms_droppable{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;color:#bfbfbf;border:1px dashed #bfbfbf}#cms_toolbar .cms_structure .cms_dragitem{cursor:move}#cms_toolbar .cms_structure .cms_dragitem-collapsable{cursor:pointer;padding-left:15px}#cms_toolbar .cms_structure .cms_dragitem-collapsable{background:url("../img/toolbar/sprite_toolbar.png") no-repeat 1px -358px}#cms_toolbar .cms_structure .cms_dragitem-expanded{background:url("../img/toolbar/sprite_toolbar.png") no-repeat 0 -389px}#cms_toolbar .cms_structure .cms_dragitem-success{position:absolute;left:-1px;top:-1px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;width:100%;height:100%;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=60);opacity:0.6}#cms_toolbar .cms_structure .cms_draggable-selected .cms_dragitem,#cms_toolbar .cms_structure .cms_draggable-selected .cms_dragitem strong{color:#0e72ec}#cms_toolbar .cms_structure .cms_draggable-selected .cms_draggable .cms_dragitem,#cms_toolbar .cms_structure .cms_draggable-selected .cms_draggable .cms_dragitem strong{color:black}#cms_toolbar .cms_structure .cms_draggable-allowed,#cms_toolbar .cms_structure .cms_draggable-hover-allowed,#cms_toolbar .cms_structure .cms_draggable-placeholder{color:#cce6b3;border-color:#cce6b3}#cms_toolbar .cms_structure .cms_draggable-hover-allowed,#cms_toolbar .cms_structure .cms_draggable-placeholder{color:white;background:rgba(102,153,51,0.2)}#cms_toolbar .cms_structure .cms_dragitem-success{border:1px solid #cce6b3;background:#cce6b3}#cms_toolbar .cms_structure .cms_draggable-disallowed,#cms_toolbar .cms_structure .cms_draggable-hover-disallowed{color:red;border:1px dashed red;background:rgba(255,0,0,0.1)}#cms_toolbar .cms_structure .cms_draggable-disabled>.cms_dragitem-collapsable{background:none;padding-left:0}body>.cms_draggable{list-style-type:none;white-space:nowrap;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:4px 5px 3px 5px;margin:0;border-color:#e6e6e6;background:white}body>.cms_draggable .cms_switcher{display:none !important}body>.cms_draggable .cms_submenu{display:none !important}body>.cms_draggable .cms_draggables{display:none !important}/*!
* @copyright: https://github.com/divio/django-cms
*/#cms_toolbar .cms_submenu{display:block;float:right;width:20px;height:15px;cursor:pointer;position:relative;background:url("../img/toolbar/sprite_toolbar.png") no-repeat 3px -152px}#cms_toolbar .cms_submenu-lang{float:right;padding:0 5px;position:relative;top:-1px;right:-1px;border:1px solid #e6e6e6;background:white;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}#cms_toolbar .cms_submenu-dropdown{display:none;zoom:1;position:absolute;right:0;top:20px;z-index:999;min-width:140px;max-height:230px;overflow:auto;border:1px solid #e6e6e6;background:white;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1)}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item{zoom:1}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item a,#cms_toolbar .cms_submenu-dropdown span{display:block;font-size:12px;line-height:15px;text-align:left;padding:4px 8px 3px 8px}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item a{color:black}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item a:hover,#cms_toolbar .cms_submenu-dropdown .cms_submenu-item a:focus{color:white;background:#0e72ec;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #0e97ec), color-stop(100%, #0e72ec));background-image:-webkit-linear-gradient(top, #0e97ec,#0e72ec);background-image:-moz-linear-gradient(top, #0e97ec,#0e72ec);background-image:-o-linear-gradient(top, #0e97ec,#0e72ec);background-image:linear-gradient(top, #0e97ec,#0e72ec)}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item a:first-child{border-top:none}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item span{cursor:default;font-weight:bold;color:black;border-top:1px solid #a6a6a6;border-bottom:1px solid #e6e6e6}#cms_toolbar .cms_submenu-dropdown .cms_submenu-item:first-child span{border-top:none}#cms_toolbar .cms_submenu-quicksearch{display:none;position:absolute;right:-5px;top:-5px;z-index:1000;cursor:default;text-align:right;height:25px;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;background:#454545 url("../img/toolbar/sprite_toolbar.png") no-repeat right -326px}#cms_toolbar .cms_submenu-quicksearch label{cursor:pointer}#cms_toolbar .cms_submenu-quicksearch input{font-size:12px;color:white;text-align:right;-webkit-appearance:none;width:109px;padding:3px 1px 1px 5px;margin-right:25px;border:none;background:none}@media (-o-min-device-pixel-ratio: 5 / 4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dppx){.cms_toolbar-item-navigation-children>a span,.cms_sideframe-btn div,.cms_clipboard ul a,.cms_clipboard-empty a,.cms_messages .cms_messages-close,.cms_modal-collapse,.cms_modal-close,.cms_modal-maximize,.cms_modal-resize,.cms_modal-breadcrumb a,.cms_modal-breadcrumb-title,.cms_toolbar-item-logo a,.cms_toolbar-trigger a,.cms_tooltip,.cms_placeholders-menu,.cms_toolbar-debug .cms_debug-bar{background-image:url("../img/toolbar/sprite_toolbar@2x.png") !important;background-size:190px !important}#cms_toolbar .cms_loader{background-image:url("../img/loader@2x.gif") !important;background-size:32px !important}.cms_submenu,.cms_submenu-quicksearch,.cms_placeholder-title:before,.cms_placeholder .cms_dragitem-collapsable,body .cms_dragitem-collapsable,.cms_placeholder .cms_dragitem-collapsed,body .cms_dragitem-collapsed{background-image:url("../img/toolbar/sprite_toolbar@2x.png") !important;background-size:190px !important}}
View
37 cms/static/cms/js/plugins/cms.base.js
@@ -29,9 +29,44 @@ $(document).ready(function () {
CMS.API.Helpers = {
// redirects to a specific url or reloads browser
- reloadBrowser: function (url, timeout) {
+ reloadBrowser: function (url, timeout, ajax) {
+ var that = this;
// is there a parent window?
var parent = (window.parent) ? window.parent : window;
+
+ // if there is an ajax reload, prioritize
+ if(ajax) {
+ // check if the url has changed, if true redirect to the new path
+ // this requires an ajax request
+ $.ajax({
+ 'async': false,
+ 'type': 'GET',
+ 'url': CMS.config.reload.url,
+ 'data': {
+ 'model': CMS.config.reload.model,
+ 'pk': CMS.config.reload.pk
+ },
+ 'success': function (response) {
+ if(parent.location.pathname !== response) {
+ // api call to the backend to check if the current path is still the same
+ that.reloadBrowser(response);
+ } else if(url === 'REFRESH_PAGE') {
+ // if on_close provides REFRESH_PAGE, only do a reload
+ that.reloadBrowser();
+ } else if(url) {
+ // on_close can also provide a url, reload to the new destination
+ that.reloadBrowser(url);
+ }
+ },
+ 'error': function (jqXHR) {
+ that.showError(jqXHR.response + ' | ' + jqXHR.status + ' ' + jqXHR.statusText);
+ }
+ });
+
+ // cancel further operations
+ return false;
+ }
+
// add timeout if provided
parent.setTimeout(function () {
(url) ? parent.location.href = url : parent.location.reload();
View
86 cms/static/cms/js/plugins/cms.modal.js
@@ -12,14 +12,14 @@ $(document).ready(function () {
implement: [CMS.API.Helpers],
options: {
+ 'onClose': false,
+ 'minHeight': 400,
+ 'minWidth': 800,
'modalDuration': 300,
+ 'newPlugin': false,
'urls': {
'css_modal': 'cms/css/plugins/cms.toolbar.modal.css'
- },
- 'minHeight': 400,
- 'minWidth': 800,
- 'onClose': false,
- 'newPlugin': false
+ }
},
initialize: function (options) {
@@ -35,9 +35,8 @@ $(document).ready(function () {
this.click = (document.ontouchstart !== null) ? 'click.cms' : 'touchend.cms';
this.maximized = false;
this.minimized = false;
- this.enforceReload = false;
- this.enforceClose = false;
this.triggerMaximized = false;
+ this.saved = false;
// if the modal is initialized the first time, set the events
if(!this.modal.data('ready')) this._events();
@@ -59,6 +58,9 @@ $(document).ready(function () {
e.preventDefault();
that._startMove(e);
});
+ this.modal.find('.cms_modal-title').bind('dblclick.cms', function () {
+ that._maximize();
+ });
this.modal.find('.cms_modal-resize').bind('mousedown.cms', function (e) {
e.preventDefault();
that._startResize(e);
@@ -153,12 +155,9 @@ $(document).ready(function () {
} else {
this._hide(100);
}
+
// handle refresh option
- if(this.options.onClose === 'REFRESH_PAGE') {
- this.reloadBrowser();
- } else if(this.options.redirectOnClose) {
- this.reloadBrowser(this.options.redirectOnClose);
- }
+ if(this.options.onClose) this.reloadBrowser(this.options.onClose, false, true);
},
// private methods
@@ -414,16 +413,14 @@ $(document).ready(function () {
var buttons = row.find('input, a');
var render = $('<span />'); // seriously jquery...
- // if there are no buttons, try again
+ // if there are no given buttons within the submit-row area
+ // scan deeper within the form itself
if(!buttons.length) {
row = iframe.contents().find('form:eq(0)');
buttons = row.find('input[type="submit"]');
buttons.attr('name', '_save')
.addClass('deletelink')
.hide();
- this.enforceReload = true;
- } else {
- this.enforceReload = false;
}
// attach relation id
@@ -446,7 +443,7 @@ $(document).ready(function () {
if(item.hasClass('default')) cls = 'cms_btn cms_btn-action';
if(item.hasClass('deletelink')) cls = 'cms_btn cms_btn-caution';
- // create the element
+ // create the element and attach events
var el = $('<div class="'+cls+' '+item.attr('class')+'">'+title+'</div>');
el.bind(that.click, function () {
if(item.is('input')) item[0].click();
@@ -455,12 +452,12 @@ $(document).ready(function () {
// trigger only when blue action buttons are triggered
if(item.hasClass('default') || item.hasClass('deletelink')) {
that.options.newPlugin = null;
- that.enforceClose = true;
- if(item.hasClass('deletelink')) {
- that.options.onClose = null;
- }
- } else {
- that.enforceClose = false;
+ // reset onClose when delete is triggered
+ if(item.hasClass('deletelink')) that.options.onClose = null;
+ // hide iframe
+ that.modal.find('.cms_modal-frame iframe').css('visibility', 'hidden');
+ // page has been saved or deleted, run checkup
+ that.saved = true;
}
});
@@ -491,7 +488,7 @@ $(document).ready(function () {
var title = this.modal.find('.cms_modal-title');
title.html(name || '&nbsp;');
- // insure previous iframe is hidden
+ // ensure previous iframe is hidden
holder.find('iframe').css('visibility', 'hidden');
// attach load event for iframe to prevent flicker effects
@@ -512,28 +509,35 @@ $(document).ready(function () {
// after iframe is loaded append css
contents.find('head').append($('<link rel="stylesheet" type="text/css" href="' + that.config.urls.static + that.options.urls.css_modal + '" />'));
- // set title of not provided
- var innerTitle = iframe.contents().find('#content h1:eq(0)');
- if(name === undefined) title.html(innerTitle.text());
- innerTitle.remove();
+ // when an error occurs, reset the saved status so the form can be checked and validated again
+ if(iframe.contents().find('.errornote').length || iframe.contents().find('.errorlist').length) {
+ that.saved = false;
+ }
- // set modal buttons
- that._setButtons($(this));
+ // when the window has been changed pressing the blue or red button, we need to run a reload check
+ if(that.saved) {
+ that.reloadBrowser(false, false, true);
+ } else {
+ // set title of not provided
+ var innerTitle = iframe.contents().find('#content h1:eq(0)');
+ if(name === undefined) title.html(innerTitle.text());
+ innerTitle.remove();
- // than show
- iframe.css('visibility','visible');
+ // than show
+ iframe.css('visibility', 'visible');
- // append ready state
- iframe.data('ready', true);
+ // append ready state
+ iframe.data('ready', true);
- // attach close event
- contents.find('body').bind('keydown.cms', function (e) {
- if(e.keyCode === 27) that.close();
- });
+ // attach close event
+ contents.find('body').bind('keydown.cms', function (e) {
+ if(e.keyCode === 27) that.close();
+ });
- // figure out if .object-tools is available
- if(contents.find('.object-tools').length) {
- contents.find('#content').css('padding-top', 38);
+ // figure out if .object-tools is available
+ if(contents.find('.object-tools').length) {
+ contents.find('#content').css('padding-top', 38);
+ }
}
});
View
2  cms/static/cms/js/plugins/cms.plugins.js
@@ -253,7 +253,7 @@ $(document).ready(function () {
var modal = new CMS.Modal({
'newPlugin': this.newPlugin || false,
'onClose': this.options.onClose || false,
- 'redirectOnClose': this.options.redirectOnClose || false,
+ 'redirectOnClose': this.options.redirectOnClose || false
});
modal.open(url, name, breadcrumb);
},
View
7 cms/static/cms/js/plugins/cms.sideframe.js
@@ -12,6 +12,7 @@ $(document).ready(function () {
implement: [CMS.API.Helpers],
options: {
+ 'onClose': false,
'sideframeDuration': 300,
'sideframeWidth': 320,
'urls': {
@@ -122,6 +123,9 @@ $(document).ready(function () {
iframe.contents().find('body').bind(that.click, function () {
$(document).trigger(that.click);
});
+
+ // attach reload event
+ that.reloadBrowser(false, false, true);
});
// cancel animation if sideframe is already shown
@@ -163,6 +167,9 @@ $(document).ready(function () {
// update settings
this.settings = this.setSettings(this.settings);
+
+ // handle refresh option
+ if(this.options.onClose) this.reloadBrowser(this.options.onClose, false, true);
},
// private methods
View
4 cms/static/cms/js/plugins/cms.structureboard.js
@@ -25,6 +25,7 @@ $(document).ready(function () {
this.toolbar = $('#cms_toolbar');
this.sortables = $('.cms_draggables'); // use global scope
this.plugins = $('.cms_plugin');
+ this.render_model = $('.cms_render_model');
this.placeholders = $('.cms_placeholder');
this.dragitems = $('.cms_draggable');
this.dragareas = $('.cms_dragarea');
@@ -274,8 +275,7 @@ $(document).ready(function () {
if(e.type === 'mouseup') clearTimeout(timer);
});
- // hide stuff
- this.plugins.hide();
+ this.plugins.not(this.render_model).hide();
this.placeholders.show();
// attach event
View
33 cms/static/cms/js/plugins/cms.toolbar.js
@@ -57,11 +57,6 @@ $(document).ready(function () {
// add toolbar ready class to body
this.body.addClass('cms_toolbar-ready');
- // check if we need to reset the current settings depending on a new release
- if(CMS.config.settings.version !== this.settings.version) {
- this.settings = this.setSettings(CMS.config.settings);
- }
-
// check if debug is true
if(CMS.config.debug) this._debug();
@@ -72,7 +67,10 @@ $(document).ready(function () {
if(CMS.config.error) this.showError(CMS.config.error);
// enforce open state if user is not logged in but requests the toolbar
- if(!CMS.config.auth) this.toggleToolbar(true);
+ if(!CMS.config.auth || CMS.config.settings.version !== this.settings.version) {
+ this.toggleToolbar(true);
+ this.settings = this.setSettings(CMS.config.settings);
+ }
// should switcher indicate that there is an unpublished page?
if(CMS.config.publisher) {
@@ -146,16 +144,17 @@ $(document).ready(function () {
});
// attach hover
- lists.find('li').bind('mouseenter mouseleave', function () {
- // reset
- lists.find('li').removeClass(hover);
-
+ lists.find('li').bind('mouseenter mouseleave', function (e) {
var el = $(this);
var parent = el.closest('.cms_toolbar-item-navigation-children');
var hasChildren = el.hasClass(children) || parent.length;
// do not attach hover effect if disabled
- if(el.hasClass(disabled)) return false;
+ // cancel event if element has already hover class
+ if(el.hasClass(disabled) || el.hasClass(hover)) return false;
+
+ // reset
+ lists.find('li').removeClass(hover);
// add hover effect
el.addClass(hover);
@@ -397,22 +396,22 @@ $(document).ready(function () {
_delegate: function (el) {
// save local vars
- var target = el.attr('data-rel');
+ var target = el.data('rel');
switch(target) {
case 'modal':
- var modal = new CMS.Modal({'onClose': el.attr('data-on-close')});
- modal.open(el.attr('href'), el.attr('data-name'));
+ var modal = new CMS.Modal({'onClose': el.data('on-close')});
+ modal.open(el.attr('href'), el.data('name'));
break;
case 'message':
- this.openMessage(el.attr('data-text'));
+ this.openMessage(el.data('text'));
break;
case 'sideframe':
- var sideframe = new CMS.Sideframe();
+ var sideframe = new CMS.Sideframe({'onClose': el.data('on-close')});
sideframe.open(el.attr('href'), true);
break;
case 'ajax':
- this.openAjax(el.attr('href'), el.attr('data-post'), el.attr('data-text'));
+ this.openAjax(el.attr('href'), el.data('post'), el.data('text'));
break;
default:
window.location.href = el.attr('href');
View
3  cms/static/cms/sass/modules/_structureboard.scss
@@ -129,6 +129,9 @@ display:none; position:absolute; top:0; right:0; width:100%; height:100%; z-inde
//.cms_placeholder .cms_draggables .cms_draggable { margin-left:0 !important; }
+// hide arrow when adding plugin-in-plugin within disabled item
+.cms_draggable-disabled > .cms_dragitem-collapsable { background:none; padding-left:0; }
+
// end of dragarea
}
View
9 cms/templates/admin/cms/page/permissions.html
@@ -1,4 +1,5 @@
{% load i18n cms_admin %}
+{% load url from future %}
{% if permission_set %}
<table>
<thead><tr>
@@ -19,19 +20,19 @@
<tr class="{% cycle 'row1' 'row2' %}">
<td class="page">
{% if meta.0 %}
- {% if meta.1 %}<a href= "/admin/cms/globalpagepermission/{{ permission.id }}/">{% trans "(global)" %}</a>
+ {% if meta.1 %}<a href="{% url 'admin:cms_globalpagepermission_change' permission.id %}">{% trans "(global)" %}</a>
{% else %}{% trans "(global)" %}
{% endif %}
{% else %}
{% ifequal permission.page_id page.id %}
{% trans "(current)" %}
{% else %}
- {% if meta.1 %}<a href= "/admin/cms/page/{{ permission.page.id }}/">{{ permission.page }}</a>
+ {% if meta.1 %}<a href="{% url 'admin:cms_page_change' permission.page.id %}">{{ permission.page }}</a>
{% else %}{{ permission.page }}
{% endif %}
{% endifequal %}
{% endif %}
- </td>
+ </td>
<td class="user">{{ permission.user|default_if_none:"-" }}</td>
<td class="group">{{ permission.group|default_if_none:"-" }}</td>
<td class="can_change">{{ permission.can_change|boolean_icon }}</td>
@@ -43,7 +44,7 @@
<td class="can_view">{{ permission.can_view|boolean_icon }}</td>
<td class="grant_on">{% if meta.0 %}{% trans "All" %}
{% else %}{{ permission.get_grant_on_display }}
- {% endif%}</td>
+ {% endif%}</td>
</tr>
{% endfor %}
</tbody>
View
3  cms/templates/admin/cms/page/tree/base.html
@@ -53,6 +53,9 @@
var msg = $({% javascript_string %}<span class="success">{% trans "Successfully moved" %}</span>{% end_javascript_string %});
node.append(msg);
msg.fadeOut(3000);
+ // check for reload changes
+ if(window.parent) window.parent.CMS.API.Helpers.reloadBrowser(false, false, true);
+
};
moveError = function(node,message){
if(message && message!="error") {
View
6 cms/templates/admin/cms/page/tree/menu_item.html
@@ -26,8 +26,8 @@
{% if page.application_urls %}<div class="col-apphook"><a href="{{ page.id }}/advanced-settings/"><span class="icon apphook-icon" title="{% blocktrans with page.application_urls as apphook%}Application: {{ apphook }}{% endblocktrans %}"></span></a></div>{% endif %}
{% for lang in site_languages %}
<div class="col-language">
- {% if has_change_permission %}
- <a href="{% if page|is_published:lang %}./{{ page.id }}/{{ lang }}/preview/{% else %}./{{ page.id }}/{{ lang }}/publish/{% endif %}" class="trigger-tooltip"{% if page|is_published:lang %} target="_top"{% endif %} title="{% blocktrans with lang|upper as language %}Edit this page in {{ language }} {% endblocktrans %}">{% tree_publish_row page lang %}</a>
+ {% if has_change_permission %}
+ <a href="{% if lang in page.languages %}./{{ page.id }}/{{ lang }}/preview/{% else %}./{{ page.id }}/?language={{ lang }}{% endif %}" class="trigger-tooltip"{% if lang in page.languages %} target="_top"{% endif %} title="{% blocktrans with lang|upper as language %}Edit this page in {{ language }} {% endblocktrans %}">{% tree_publish_row page lang %}</a>
{% if lang in page.languages %}
<div class="language-tooltip" hidden="hidden">
<a href="./≈/{{ lang }}/unpublish/">{% trans "Unpublish" %}</a>
@@ -79,4 +79,4 @@
</span>
</div>
</div>
-</div>
+</div>
View
2  cms/templates/cms/toolbar/items/item_modal.html
@@ -1,3 +1,3 @@
<li class="{% if active %}cms_toolbar-item-navigation-active{% endif %}{% if disabled %} cms_toolbar-item-navigation-disabled{% endif %} {{ extra_classes|join:' ' }}">
- <a href="{{ url }}" data-rel="modal"{% if close_on_url %} data-close-on-url="{{ close_on_url }}"{% endif %}{% if on_close %} data-on-close="{{ on_close }}"{% endif %}><span>{{ name }}</span></a>
+ <a href="{{ url }}" data-rel="modal"{% if on_close %} data-on-close="{{ on_close }}"{% endif %}><span>{{ name }}</span></a>
</li>
View
2  cms/templates/cms/toolbar/items/item_sideframe.html
@@ -1,3 +1,3 @@
<li class="{% if active %}cms_toolbar-item-navigation-active{% endif %}{% if disabled %} cms_toolbar-item-navigation-disabled{% endif %} {{ extra_classes|join:' ' }}">
- <a href="{{ url }}" data-rel="sideframe"{% if close_on_url %} data-close-on-url="{{ close_on_url }}"{% endif %}{% if on_close %} data-on-close="{{ on_close }}"{% endif %}><span>{{ name }}</span></a>
+ <a href="{{ url }}" data-rel="sideframe"{% if on_close %} data-on-close="{{ on_close }}"{% endif %}><span>{{ name }}</span></a>
</li>
View
5 cms/templates/cms/toolbar/plugin.html
@@ -2,7 +2,7 @@
{% load i18n l10n sekizai_tags static %}
{% load url from future %}
-<div class="cms_plugin cms_plugin-{% if generic %}{{ generic.app_label }}-{{ generic.module_name }}-{% if attribute_name %}{{ attribute_name|slugify }}-{% endif %}{% endif %}{{ instance.pk|unlocalize }}{% if render_model_icon %} cms_render_model_icon{% elif render_model_add %} cms_render_model_add{% endif %}">{% if render_model_icon %}<img src="{% static 'cms/img/toolbar/render_model_placeholder.png' %}">{% elif render_model_add %}<img src="{% static 'cms/img/toolbar/render_model_placeholder.png' %}">{% else %}{{ rendered_content }}{% endif %}</div>
+<div class="cms_plugin cms_plugin-{% if generic %}{{ generic.app_label }}-{{ generic.module_name }}-{% if attribute_name %}{{ attribute_name|slugify }}-{% endif %}{% endif %}{{ instance.pk|unlocalize }}{% if render_model_icon %} cms_render_model_icon{% elif render_model %} cms_render_model{% elif render_model_block %} cms_render_model cms_render_model_block{% elif render_model_add %} cms_render_model_add{% endif %}">{% if render_model_icon %}<img src="{% static 'cms/img/toolbar/render_model_placeholder.png' %}">{% elif render_model_add %}<img src="{% static 'cms/img/toolbar/render_model_placeholder.png' %}">{% else %}{{ rendered_content }}{% endif %}</div>
{% endspaceless %}{% addtoblock "js" %}
<script>
@@ -21,8 +21,7 @@
'plugin_order': '{{ instance.plugin_order }}',{% language request.toolbar.toolbar_language %}
'plugin_breadcrumb': {{ instance.get_breadcrumb_json|default:"[]" }},
'plugin_restriction': [{% for cls in allowed_child_classes %}"{{ cls }}"{% if not forloop.last %},{% endif %}{% endfor %}],
- 'onClose': {% if refresh_page %}'REFRESH_PAGE'{% else %}false{% endif %},
- 'redirectOnClose': {% if redirect_on_close %}'{{ redirect_on_close }}'{% else %}false{% endif %},
+ 'onClose': {% if refresh_page %}'REFRESH_PAGE'{% else %}{% if redirect_on_close %}'{{ redirect_on_close }}'{% else %}false{% endif %}{% endif %},
'urls': {
'add_plugin': '{% if add_url %}{{ add_url }}{% else %}{% url "admin:cms_page_add_plugin" %}}{% endif %}',
'edit_plugin': '{% if edit_url %}{{ edit_url }}{% else %}{% url "admin:cms_page_edit_plugin" instance.pk %}{% endif %}',
View
1  cms/templates/cms/toolbar/toolbar.html
@@ -111,6 +111,5 @@
<div class="cms_structure-dimmer"></div>
</div>
{# end: structure #}
-
</div>
{% endlanguage %}
View
5 cms/templates/cms/toolbar/toolbar_javascript.html
@@ -57,6 +57,11 @@
'id': '{{ request.toolbar.clipboard.pk|unlocalize }}',
'url': '{% if request.toolbar.clipboard.pk %}{% url "admin:cms_page_clear_placeholder" request.toolbar.clipboard.pk %}{% endif %}'
},
+ 'reload': {
+ 'url': '{% url "admin:cms_page_resolve" %}',
+ 'model': '{{ request.toolbar.get_object_model }}',
+ 'pk': '{{ request.toolbar.get_object_pk }}'
+ },
'messages': '{% if messages %}{% for message in messages %}{{ message }}{% endfor %}{% endif %}',
'error': '{% if request.toolbar.login_form.errors %}{% blocktrans %}<strong>Login failed.</strong> Please check your credentials and try again.{% endblocktrans %}{% endif %}',
'publisher': '{% if not request.current_page.publisher_is_draft and request.current_page.publisher_draft.is_dirty and user.is_authenticated %}{% trans "This page has unpublished changes." %}{% endif %}'
View
2  cms/templatetags/cms_tags.py
@@ -832,6 +832,7 @@ def get_context(self, context, instance, attribute, edit_fields,
extra_context = self._get_data_context(context, instance, attribute,
edit_fields, language, filters,
view_url, view_method)
+ extra_context['render_model'] = True
return extra_context
register.tag(CMSEditableObject)
@@ -943,6 +944,7 @@ def get_context(self, context, instance, edit_fields, language,
extra_context = self._get_empty_context(context, instance, edit_fields,
language, view_url, view_method)
extra_context['instance'] = instance
+ extra_context['render_model_block'] = True
return extra_context
register.tag(CMSEditableObjectBlock)
View
1  cms/test_utils/cli.py
@@ -30,7 +30,6 @@ def configure(db_url, **extra):
DATABASES={
'default': DB
},
- SESSION_ENGINE="django.contrib.sessions.backends.cache",
SITE_ID=1,
USE_I18N=True,
MEDIA_ROOT='/media/',
View
2  cms/tests/admin.py
@@ -1195,7 +1195,7 @@ def test_render_edit_mode(self):
user = self.get_superuser()
self.assertEqual(Placeholder.objects.all().count(), 4)
with self.login_user_context(user):
- with self.assertNumQueries(FuzzyInt(40, 61)):
+ with self.assertNumQueries(FuzzyInt(40, 63)):
output = force_unicode(self.client.get('/en/?edit').content)
self.assertIn('<b>Test</b>', output)
self.assertEqual(Placeholder.objects.all().count(), 9)
View
89 cms/tests/cache.py
@@ -96,14 +96,93 @@ def test_no_cache_plugin(self):
plugin_pool.unregister_plugin(NoCachePlugin)
+
def test_cache_page(self):
- page1 = create_page('test page 1', 'nav_playground.html', 'en',
- published=True)
+ from cms.views import _get_cache_version
+ from cms.utils import get_cms_setting
+ from django.conf import settings
+
+ # We'll store the old MW so that we can play nice with the other tests
+ old_middleware = settings.MIDDLEWARE_CLASSES[:]
+
+ # Clear the entire cache for a clean slate
+ cache.clear()
+
+ # Ensure that we're testing in an environment WITHOUT the MW cache...
+ exclude = [
+ 'django.middleware.cache.UpdateCacheMiddleware',
+ 'django.middleware.cache.FetchFromCacheMiddleware'
+ ]
+ settings.MIDDLEWARE_CLASSES[:] = [mw for mw in settings.MIDDLEWARE_CLASSES if mw not in exclude]
+
+ # Silly to do these tests if this setting isn't True
+ page_cache_setting = get_cms_setting('PAGE_CACHE')
+ self.assertTrue(page_cache_setting)
+
+ # Create a test page
+ page1 = create_page('test page 1', 'nav_playground.html', 'en', published=True)
+
+ # Add some content
placeholder = page1.placeholders.filter(slot="body")[0]
add_plugin(placeholder, "TextPlugin", 'en', body="English")
add_plugin(placeholder, "TextPlugin", 'de', body="Deutsch")
- with self.assertNumQueries(FuzzyInt(10,20)):
- self.client.get('/en/')
+
+ # Create a request object
+ request = self.get_request(page1.get_path(), 'en')
+
+ # Ensure that user is NOT authenticated
+ self.assertFalse(request.user.is_authenticated())
+
+ # Test that the page is initially uncached
+ with self.assertNumQueries(FuzzyInt(1, 20)):
+ response = self.client.get('/en/')
+ self.assertEqual(response.status_code, 200)
+
+ #
+ # Test that subsequent requests of the same page are cached by
+ # asserting that they require fewer queries.
+ #
with self.assertNumQueries(0):
- self.client.get('/en/')
+ response = self.client.get('/en/')
+ self.assertEqual(response.status_code, 200)
+
+ #
+ # Test that the cache is invalidated on unpublishing the page
+ #
+ old_version = _get_cache_version()
+ page1.unpublish('en')
+ self.assertGreater(_get_cache_version(), old_version)
+
+ #
+ # Test that this means the page is actually not cached.
+ #
+ page1.publish('en')
+ with self.assertNumQueries(FuzzyInt(1, 20)):
+ response = self.client.get('/en/')
+ self.assertEqual(response.status_code, 200)
+
+ #
+ # Test that the above behavior is different when CMS_PAGE_CACHE is
+ # set to False (disabled)
+ #
+ cache.clear()
+ settings.CMS_PAGE_CACHE = False
+
+ # Test that the page is initially uncached
+ with self.assertNumQueries(FuzzyInt(1, 20)):
+ response = self.client.get('/en/')
+ self.assertEqual(response.status_code, 200)
+
+ #
+ # Test that subsequent requests of the same page are still requires DB
+ # access.
+ #
+ with self.assertNumQueries(FuzzyInt(1, 20)):
+ response = self.client.get('/en/')
+ self.assertEqual(response.status_code, 200)
+
+ #
+ # Let's reset the original middleware for the remaining tests...
+ #
+ settings.MIDDLEWARE_CLASSES = old_middleware[:]
View
36 cms/tests/i18n.py
@@ -1,3 +1,4 @@
+from cms import api
from cms.test_utils.testcases import SettingsOverrideTestCase
from cms.utils import i18n
@@ -270,3 +271,38 @@ def test_get_languages_undefined_site(self):
for lang in result:
self.assertEqual(lang['public'], True)
self.assertEqual(lang['hide_untranslated'], False)
+
+
+class TestLanguageFallbacks(SettingsOverrideTestCase):
+
+ settings_overrides = {
+ 'LANGUAGE_CODE': 'en',
+ 'LANGUAGES': (('fr', 'French'),
+ ('en', 'English'),
+ ('de', 'German'),
+ ('es', 'Spanish')),
+ 'CMS_LANGUAGES': {
+ 1: [ {'code' : 'en',
+ 'name': 'English',
+ 'public': False},
+ {'code': 'fr',
+ 'name': 'French',
+ 'public': True},
+ ],
+ 'default': {
+ 'fallbacks': ['en', 'fr'],
+ 'redirect_on_fallback': False,
+ 'public': True,
+ 'hide_untranslated': False,
+ }
+ },
+ 'SITE_ID': 1,
+ }
+
+ def test_language_code(self):
+ home = api.create_page("home", "nav_playground.html", "fr", published=True)
+ response = self.client.get('/')
+ self.assertEqual(response.status_code, 302)
+ response = self.client.get('/en/')
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, '/fr/')
View
2  cms/tests/multilingual.py
@@ -259,7 +259,7 @@ def test_language_fallback(self):
lang_settings[1][1]['redirect_on_fallback'] = False
with SettingsOverride(CMS_LANGUAGES=lang_settings):
response = self.client.get("/de/")
- self.assertEquals(response.status_code, 200)
+ self.assertEquals(response.status_code, 302)
def test_no_english_defined(self):
with SettingsOverride(TEMPLATE_CONTEXT_PROCESSORS=[],
View
225 cms/tests/toolbar.py
@@ -1,8 +1,10 @@
from __future__ import with_statement
+from cms.models import UserSettings
import re
from django.template.defaultfilters import truncatewords
import datetime
+from cms.models import Page
from django.template.defaultfilters import truncatewords
from cms.views import details
import re
@@ -11,7 +13,7 @@
from cms.toolbar.items import ToolbarAPIMixin, LinkItem, ItemSearchResult, Break, SubMenu
from cms.toolbar.toolbar import CMSToolbar
from cms.middleware.toolbar import ToolbarMiddleware
-from cms.test_utils.testcases import SettingsOverrideTestCase
+from cms.test_utils.testcases import SettingsOverrideTestCase, URL_CMS_PAGE_ADD, URL_CMS_PAGE_CHANGE
from cms.test_utils.util.context_managers import SettingsOverride
from django.contrib.auth.models import AnonymousUser, User, Permission
from django.test import TestCase
@@ -263,6 +265,20 @@ def test_user_settings(self):
response = self.client.get('/en/admin/cms/usersettings/')
self.assertEqual(response.status_code, 200)
+ def test_remove_lang(self):
+ page = create_page('test', 'nav_playground.html', 'en', published=True)
+ superuser = self.get_superuser()
+ with self.login_user_context(superuser):
+ response = self.client.get('/en/?edit')
+ self.assertEqual(response.status_code, 200)
+ setting = UserSettings.objects.get(user=superuser)
+ setting.language = 'it'
+ setting.save()
+ with SettingsOverride(LANGUAGES=(('en', 'english'),)):
+ response = self.client.get('/en/?edit')
+ self.assertEqual(response.status_code, 200)
+ self.assertNotContains(response, '/it/')
+
def test_get_alphabetical_insert_position(self):
page = create_page("toolbar-page", "nav_playground.html", "en",
published=True)
@@ -290,6 +306,37 @@ def test_get_alphabetical_insert_position(self):
beta_position = admin_menu.get_alphabetical_insert_position('menu-beta', SubMenu)
self.assertEqual(beta_position, gamma_position)
+ def test_page_create_redirect(self):
+ superuser = self.get_superuser()
+ page = create_page("home", "nav_playground.html", "en",
+ published=True)
+ resolve_url = reverse('admin:cms_page_resolve')
+ with self.login_user_context(superuser):
+ response = self.client.post(resolve_url, {'pk': '', 'model': 'cms.page'})
+ self.assertEqual(response.content.decode('utf-8'), '/')
+ page_data = self.get_new_page_data()
+ response = self.client.post(URL_CMS_PAGE_ADD, page_data)
+
+ response = self.client.post(resolve_url, {'pk': Page.objects.all()[2].pk, 'model': 'cms.page'})
+ self.assertEqual(response.content.decode('utf-8'), '/en/test-page-1/')
+
+ def test_page_edit_redirect(self):
+ page1 = create_page("home", "nav_playground.html", "en",
+ published=True)
+ page2 = create_page("test", "nav_playground.html", "en",
+ published=True)
+ superuser = self.get_superuser()
+ with self.login_user_context(superuser):
+ page_data = self.get_new_page_data()
+ response = self.client.post(URL_CMS_PAGE_CHANGE % page2.pk, page_data)
+ url = reverse('admin:cms_page_resolve')
+ response = self.client.post(url, {'pk': page1.pk, 'model': 'cms.page'})
+ self.assertEqual(response.content.decode('utf-8'), '/en/test-page-1/')
+ response = self.client.post(url, {'pk': page1.pk, 'model': 'cms.page'})
+ self.assertEqual(response.content.decode('utf-8'), '/en/')
+ response = self.client.post(url, {'pk': page1.pk, 'model': 'cms.page'})
+ self.assertEqual(response.content.decode('utf-8'), '/')
+
class EditModelTemplateTagTest(ToolbarTestBase):
urls = 'cms.test_utils.project.placeholderapp_urls'
@@ -330,8 +377,10 @@ def test_edit(self):
ex1.save()
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'char_1', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'char_1', ex1.pk))
def test_invalid_item(self):
user = self.get_staff()
@@ -348,7 +397,9 @@ def test_invalid_item(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<div class="cms_plugin cms_plugin-%s"></div>' % ex1.pk)
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s cms_render_model"></div>' % ex1.pk)
def test_as_varname(self):
user = self.get_staff()
@@ -365,7 +416,9 @@ def test_as_varname(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertNotContains(response, '<div class="cms_plugin cms_plugin-%s"></div>' % ex1.pk)
+ self.assertNotContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s cms_render_model"></div>' % ex1.pk)
def test_filters(self):
user = self.get_staff()
@@ -383,8 +436,10 @@ def test_filters(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">%s</div></h1>' % (
- 'placeholderapp', 'example1', 'char_1', ex1.pk, truncatewords(ex1.char_1, 2)))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">%s</div></h1>' % (
+ 'placeholderapp', 'example1', 'char_1', ex1.pk, truncatewords(ex1.char_1, 2)))
def test_filters_date(self):
user = self.get_staff()
@@ -403,8 +458,11 @@ def test_filters_date(self):
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">%s</div></h1>' % (
- 'placeholderapp', 'example1', 'date_field', ex1.pk, ex1.date_field.strftime("%Y-%m-%d")))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">%s</div></h1>' % (
+ 'placeholderapp', 'example1', 'date_field', ex1.pk,
+ ex1.date_field.strftime("%Y-%m-%d")))
template_text = '''{% extends "base.html" %}
{% load cms_tags %}
@@ -414,8 +472,11 @@ def test_filters_date(self):
{% endblock content %}
'''
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">%s</div></h1>' % (
- 'placeholderapp', 'example1', 'date_field', ex1.pk, ex1.date_field.strftime("%Y %m %d")))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">%s</div></h1>' % (
+ 'placeholderapp', 'example1', 'date_field', ex1.pk,
+ ex1.date_field.strftime("%Y %m %d")))
def test_filters_notoolbar(self):
user = self.get_staff()
@@ -433,7 +494,8 @@ def test_filters_notoolbar(self):
'''
request = self.get_page_request(page, user, edit=False)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1>%s</h1>' % truncatewords(ex1.char_1, 2))
+ self.assertContains(response,
+ '<h1>%s</h1>' % truncatewords(ex1.char_1, 2))
def test_no_cms(self):
user = self.get_staff()
@@ -449,10 +511,11 @@ def test_no_cms(self):
'''
request = self.get_page_request('', user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response,
- '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model_icon"><img src="/static/cms/img/toolbar/render_model_placeholder.png"></div>' % (
- 'placeholderapp', 'example1', ex1.pk))
- self.assertContains(response, '\'redirectOnClose\': false,')
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model_icon"><img src="/static/cms/img/toolbar/render_model_placeholder.png"></div>' % (
+ 'placeholderapp', 'example1', ex1.pk))
+ self.assertContains(response, "'onClose': 'REFRESH_PAGE',")
def test_icon_tag(self):
user = self.get_staff()
@@ -469,9 +532,10 @@ def test_icon_tag(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response,
- '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model_icon"><img src="/static/cms/img/toolbar/render_model_placeholder.png"></div>' % (
- 'placeholderapp', 'example1', ex1.pk))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model_icon"><img src="/static/cms/img/toolbar/render_model_placeholder.png"></div>' % (
+ 'placeholderapp', 'example1', ex1.pk))
def test_add_tag(self):
user = self.get_staff()
@@ -488,9 +552,10 @@ def test_add_tag(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response,
- '<div class="cms_plugin cms_plugin-%s-%s-add-%s cms_render_model_add"><img src="/static/cms/img/toolbar/render_model_placeholder.png"></div>' % (
- 'placeholderapp', 'example1', ex1.pk))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-add-%s cms_render_model_add"><img src="/static/cms/img/toolbar/render_model_placeholder.png"></div>' % (
+ 'placeholderapp', 'example1', ex1.pk))
def test_block_tag(self):
user = self.get_staff()
@@ -517,9 +582,10 @@ def test_block_tag(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertNotContains(response,
- '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model_icon"><img src="/static/cms/img/toolbar/render_model_icon.png"></div>' % (
- 'placeholderapp', 'example1', ex1.pk))
+ self.assertNotContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model_icon"><img src="/static/cms/img/toolbar/render_model_icon.png"></div>' % (
+ 'placeholderapp', 'example1', ex1.pk))
# This template does not render anything as content is saved in a
# variable and inserted in the page afterwards
@@ -541,8 +607,10 @@ def test_block_tag(self):
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
# Assertions on the content of the block tag
- self.assertContains(response,
- '<div class="cms_plugin cms_plugin-%s-%s-%s">' % ('placeholderapp', 'example1', ex1.pk))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model cms_render_model_block">' % (
+ 'placeholderapp', 'example1', ex1.pk))
self.assertContains(response, '<h1>%s - %s</h1>' % (ex1.char_1, ex1.char_2))
self.assertContains(response, '<span class="date">%s</span>' % (ex1.date_field.strftime("%Y")))
self.assertContains(response, '<a href="%s">successful if</a></div>' % (reverse('detail', args=(ex1.pk,))))
@@ -565,8 +633,10 @@ def test_block_tag(self):
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
# Assertions on the content of the block tag
- self.assertContains(response,
- '<div class="cms_plugin cms_plugin-%s-%s-%s">' % ('placeholderapp', 'example1', ex1.pk))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-%s cms_render_model cms_render_model_block">' % (
+ 'placeholderapp', 'example1', ex1.pk))
self.assertContains(response, '<h1>%s - %s</h1>' % (ex1.char_1, ex1.char_2))
self.assertContains(response, '<span class="date">%s</span>' % (ex1.date_field.strftime("%Y")))
self.assertContains(response, '<a href="%s">successful if</a></div>' % (reverse('detail', args=(ex1.pk,))))
@@ -584,7 +654,10 @@ def test_block_tag(self):
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
# Assertions on the content of the block tag
- self.assertContains(response, '<div class="cms_plugin cms_plugin-%s-%s-changelist-%s">' % ('placeholderapp', 'example1', ex1.pk))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-changelist-%s cms_render_model cms_render_model_block">' % (
+ 'placeholderapp', 'example1', ex1.pk))
def test_invalid_attribute(self):
user = self.get_staff()
@@ -601,20 +674,24 @@ def test_invalid_attribute(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<div class="cms_plugin cms_plugin-%s-%s-%s-%s"></div>' % (
- 'placeholderapp', 'example1', 'fake_field', ex1.pk))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model"></div>' % (
+ 'placeholderapp', 'example1', 'fake_field', ex1.pk))
# no attribute
template_text = '''{% extends "base.html" %}
{% load cms_tags %}
-{% block content %}CIAOOOO
+{% block content %}
<h1>{% render_model instance "" %}</h1>
{% endblock content %}
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<div class="cms_plugin cms_plugin-%s"></div>' % ex1.pk)
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-%s cms_render_model"></div>' % ex1.pk)
def test_callable_item(self):
user = self.get_staff()
@@ -631,8 +708,10 @@ def test_callable_item(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
def test_view_method(self):
user = self.get_staff()
@@ -649,8 +728,10 @@ def test_view_method(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
def test_method_attribute(self):
user = self.get_staff()
@@ -668,8 +749,10 @@ def test_method_attribute(self):
request = self.get_page_request(page, user, edit=True)
ex1.set_static_url(request)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
def test_admin_url(self):
user = self.get_staff()
@@ -686,8 +769,9 @@ def test_admin_url(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
def test_admin_url_extra_field(self):
user = self.get_staff()
@@ -704,8 +788,9 @@ def test_admin_url_extra_field(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
self.assertContains(response, "/admin/placeholderapp/example1/edit-field/%s/en/" % ex1.pk)
self.assertTrue(re.search(self.edit_fields_rx % "char_2", response.content.decode('utf8')))
@@ -724,8 +809,10 @@ def test_admin_url_multiple_fields(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
self.assertContains(response, "/admin/placeholderapp/example1/edit-field/%s/en/" % ex1.pk)
self.assertTrue(re.search(self.edit_fields_rx % "char_1", response.content.decode('utf8')))
self.assertTrue(re.search(self.edit_fields_rx % "char_1%2Cchar_2", response.content.decode('utf8')))
@@ -745,8 +832,10 @@ def test_instance_method(self):
'''
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
def test_item_from_context(self):
user = self.get_staff()
@@ -764,8 +853,10 @@ def test_item_from_context(self):
request = self.get_page_request(page, user, edit=True)
response = detail_view(request, ex1.pk, template_string=template_text,
item_name="callable_item")
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">char_1</div></h1>' % (
- 'placeholderapp', 'example1', 'callable_item', ex1.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">char_1</div></h1>' % (
+ 'placeholderapp', 'example1', 'callable_item', ex1.pk))
def test_edit_field(self):
from django.contrib.admin import site
@@ -818,8 +909,10 @@ def test_multi_edit(self):
request = self.get_page_request(page, user, edit=True)
response = detail_view_multi(request, exm.pk)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">one</div></h1>' % (
- 'placeholderapp', 'multilingualexample1', 'char_1', exm.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">one</div></h1>' % (
+ 'placeholderapp', 'multilingualexample1', 'char_1', exm.pk))
self.assertContains(response, "/admin/placeholderapp/multilingualexample1/edit-field/%s/en/" % exm.pk)
self.assertTrue(re.search(self.edit_fields_rx % "char_1", response.content.decode('utf8')))
self.assertTrue(re.search(self.edit_fields_rx % "char_1%2Cchar_2", response.content.decode('utf8')))
@@ -827,8 +920,10 @@ def test_multi_edit(self):
with SettingsOverride(LANGUAGE_CODE="fr"):
request = self.get_page_request(title.page, user, edit=True, lang_code="fr")
response = detail_view_multi(request, exm.pk)
- self.assertContains(response, '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s">un</div></h1>' % (
- 'placeholderapp', 'multilingualexample1', 'char_1', exm.pk))
+ self.assertContains(
+ response,
+ '<h1><div class="cms_plugin cms_plugin-%s-%s-%s-%s cms_render_model">un</div></h1>' % (
+ 'placeholderapp', 'multilingualexample1', 'char_1', exm.pk))
self.assertContains(response, "/admin/placeholderapp/multilingualexample1/edit-field/%s/fr/" % exm.pk)
self.assertTrue(re.search(self.edit_fields_rx % "char_1%2Cchar_2", response.content.decode('utf8')))
@@ -882,12 +977,21 @@ def test_edit_page(self):
page.reload()
request = self.get_page_request(page, user, edit=True)
response = details(request, '')
- self.assertContains(response, '<div class="cms_plugin cms_plugin-cms-page-get_page_title-%s">%s</div>' % (
- page.pk, page.get_page_title(language)))
- self.assertContains(response, '<div class="cms_plugin cms_plugin-cms-page-get_menu_title-%s">%s</div>' % (
- page.pk, page.get_menu_title(language)))
- self.assertContains(response, '<div class="cms_plugin cms_plugin-cms-page-get_title-%s">%s</div>' % (page.pk, page.get_title(language)))
- self.assertContains(response, '<div class="cms_plugin cms_plugin-cms-page-changelist-%s"><h3>Menu</h3></div>' % page.pk)
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-cms-page-get_page_title-%s cms_render_model">%s</div>' % (
+ page.pk, page.get_page_title(language)))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-cms-page-get_menu_title-%s cms_render_model">%s</div>' % (
+ page.pk, page.get_menu_title(language)))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-cms-page-get_title-%s cms_render_model">%s</div>' % (
+ page.pk, page.get_title(language)))
+ self.assertContains(
+ response,
+ '<div class="cms_plugin cms_plugin-cms-page-changelist-%s cms_render_model cms_render_model_block"><h3>Menu</h3></div>' % page.pk)
class ToolbarAPITests(TestCase):
@@ -933,3 +1037,4 @@ def test_item_search_result(self):
result += 2
self.assertEqual(result.item, item)
self.assertEqual(result.index, 4)
+
View
27 cms/toolbar/toolbar.py
@@ -45,6 +45,7 @@ def __init__(self, request):
self.build_mode = self.is_staff and self.request.session.get('cms_build', False)
self.use_draft = self.is_staff and self.edit_mode or self.build_mode
self.show_toolbar = self.is_staff or self.request.session.get('cms_edit', False)
+ self.obj = None
if settings.USE_I18N:
self.language = get_language_from_request(request)
else:
@@ -62,7 +63,12 @@ def __init__(self, request):
placeholder.save()
user_settings.clipboard = placeholder
user_settings.save()
- self.toolbar_language = user_settings.language
+ if (settings.USE_I18N and user_settings.language in dict(settings.LANGUAGES)) or (
+ not settings.USE_I18N and user_settings.language == settings.LANGUAGE_CODE):
+ self.toolbar_language = user_settings.language
+ else:
+ user_settings.language = self.language
+ user_settings.save()
self.clipboard = user_settings.clipboard
with force_language(self.language):
try:
@@ -78,7 +84,8 @@ def __init__(self, request):
if app_name in self.view_name and len(key) > len(app_key):
app_key = key
for key in toolbars:
- self.toolbars[key] = toolbars[key](self.request, self, key == app_key, app_key)
+ toolbar = toolbars[key](self.request, self, key == app_key, app_key)
+ self.toolbars[key] = toolbar
@property
def csrf_token(self):
@@ -110,6 +117,20 @@ def add_button_list(self, identifier=None, extra_classes=None, side=LEFT, positi
self.add_item(item, position=position)
return item
+ def set_object(self, obj):
+ if not self.obj:
+ self.obj = obj
+
+ def get_object_model(self):
+ if self.obj:
+ return "{0}.{1}".format(self.obj._meta.app_label, self.obj._meta.object_name).lower()
+ return ''
+
+ def get_object_pk(self):
+ if self.obj:
+ return self.obj.pk
+ return ''
+
# Internal API
def _add_item(self, item, position):
@@ -160,6 +181,8 @@ def populate(self):
# never populate the toolbar on is_staff=False
if not self.is_staff:
return
+ if self.request.session.get('cms_log_latest', False):
+ del self.request.session['cms_log_latest']
self._call_toolbar('populate')
def post_template_populate(self):
View
1  cms/toolbar_base.py
@@ -6,6 +6,7 @@
class CMSToolbar(object):
+
def __init__(self, request, toolbar, is_current_app, app_path):
self.request = request
self.toolbar = toolbar
View
100 cms/views.py
@@ -22,6 +22,8 @@
from django.utils.timezone import get_current_timezone_name
+CMS_PAGE_CACHE_VERSION_KEY = 'CMS_PAGE_CACHE_VERSION'
+
def _handle_no_page(request, slug):
if not slug and settings.DEBUG:
return TemplateResponse(request, "cms/welcome.html", RequestContext(request))
@@ -35,9 +37,17 @@ def details(request, slug):
"""
from django.core.cache import cache
- if get_cms_setting("PAGE_CACHE") and (not hasattr(request, 'toolbar') or (
- not request.toolbar.edit_mode and not request.toolbar.show_toolbar and not request.user.is_authenticated())):
- cache_content = cache.get(_get_cache_key(request))
+ if get_cms_setting("PAGE_CACHE") and (
+ not hasattr(request, 'toolbar') or (
+ not request.toolbar.edit_mode and
+ not request.toolbar.show_toolbar and
+ not request.user.is_authenticated()
+ )
+ ):
+ cache_content = cache.get(
+ _get_cache_key(request),
+ version=_get_cache_version()
+ )
if not cache_content is None:
content, headers = cache_content
response = HttpResponse(content)
@@ -95,7 +105,7 @@ def details(request, slug):
found = False
for alt_lang in get_fallback_languages(current_language):
if alt_lang in available_languages:
- if get_redirect_on_fallback(current_language):
+ if get_redirect_on_fallback(current_language) or slug == "":
with force_language(alt_lang):
path = page.get_absolute_url(language=alt_lang, fallback=True)
# In the case where the page is not available in the
@@ -152,6 +162,8 @@ def details(request, slug):
# permission checks
if page.login_required and not request.user.is_authenticated():
return redirect_to_login(urlquote(request.get_full_path()), settings.LOGIN_URL)
+ if hasattr(request, 'toolbar'):
+ request.toolbar.set_object(page)
template_name = get_template_from_request(request, page, no_current_page=True)
# fill the context
@@ -207,9 +219,22 @@ def _cache_page(response):
if not save_cache:
response
if save_cache:
- cache.set(_get_cache_key(request), (response.content, response._headers),
- get_cms_setting('CACHE_DURATIONS')['content'])
+ version = _get_cache_version()
+ ttl = get_cms_setting('CACHE_DURATIONS')['content']
+ cache.set(
+ _get_cache_key(request),
+ (response.content, response._headers),
+ ttl,
+ version=version
+ )
+ # See note in invalidate_cms_page_cache()
+ cache.set(
+ CMS_PAGE_CACHE_VERSION_KEY,
+ version,
+ ttl
+ )
+
def _get_cache_key(request):
#md5 key of current path
@@ -224,4 +249,65 @@ def _get_cache_key(request):
# Hence this paranoid conversion to create a valid cache key.
tz_name = force_text(get_current_timezone_name(), errors='ignore')
cache_key += '.%s' % tz_name.encode('ascii', 'ignore').decode('ascii').replace(' ', '_')
- return cache_key
+ return cache_key
+
+def _get_cache_version():
+ from django.core.cache import cache
+
+ '''
+ Returns the current page cache version, explicitly setting one if not
+ defined.
+ '''
+
+ version = cache.get(CMS_PAGE_CACHE_VERSION_KEY)
+
+ if version:
+ return version
+ else:
+ cache.set(
+ CMS_PAGE_CACHE_VERSION_KEY,
+ 1,
+ get_cms_setting('CACHE_DURATIONS')['content']
+ )
+ return 1
+
+
+def invalidate_cms_page_cache():
+ from django.core.cache import cache
+
+ '''
+ Invalidates the CMS PAGE CACHE.
+ '''
+
+ #
+ # NOTE: We're using a cache versioning strategy for invalidating the page
+ # cache when necessary. Instead of wiping all the old entries, we simply
+ # increment the version number rendering all previous entries
+ # inaccessible and left to expire naturally.
+ #
+ # ALSO NOTE: According to the Django documentation, a timeout value of
+ # `None' (in version 1.6+) is supposed to mean "cache forever", however,
+ # this is actually only implemented as only slightly less than 30 days in
+ # some backends (memcached, in particular). In older Djangos, `None' means
+ # "use default value". To avoid issues arising from different Django
+ # versions and cache backend implementations, we will explicitly set the
+ # lifespan of the CMS_PAGE_CACHE_VERSION entry to whatever is set in
+ # settings.CACHE_DURATIONS['content']. This allows users to adjust as
+ # necessary for their backend.
+ #
+ # To prevent writing cache entries that will live longer than our version
+ # key, we will always re-write the current version number into the cache
+ # just after we write any new cache entries, thus ensuring that the
+ # version number will always outlive any entries written against that
+ # version. This is a cheap operation.
+ #
+ # If there are no new cache writes before the version key expires, its
+ # perfectly OK, since any previous entries cached against that version
+ # will have also expired, so, it'd be pointless to try to access them
+ # anyway.
+ #
+ try:
+ cache.incr(CMS_PAGE_CACHE_VERSION_KEY)
+ except ValueError:
+ # Key doesn't exist, so just set it to the default
+ cache.set(CMS_PAGE_CACHE_VERSION_KEY, 1, get_cms_setting('CACHE_DURATIONS')['content'])
View
80 develop.py
@@ -1,6 +1,6 @@
#!/bin/env python
-from __future__ import print_function
-
+from __future__ import print_function, with_statement
+import contextlib
import multiprocessing
import pkgutil
import pyclbr
@@ -17,15 +17,15 @@
from cms.test_utils.cli import configure
from cms.test_utils.tmpdir import temp_dir
-__doc__ = '''django CMS development helper script.
+__doc__ = '''django CMS development helper script.
To use a different database, set the DATABASE_URL environment variable to a
dj-database-url compatible value.
Usage:
- develop.py test [--parallel | --failfast] [--migrate] [<test-label>...]
- develop.py timed test [test-label...]
- develop.py isolated test [<test-label>...] [--parallel] [--migrate]
+ develop.py test [--parallel | --failfast] [--migrate] [<test-label>...] [--xvfb]
+ develop.py timed test [test-label...] [--xvfb]
+ develop.py isolated test [<test-label>...] [--parallel] [--migrate] [--xvfb]
develop.py server [--port=<port>] [--bind=<bind>] [--migrate]
develop.py shell
develop.py compilemessages
@@ -39,11 +39,11 @@
--failfast Stop tests on first failure (only if not --parallel).
--port=<port> Port to listen on [default: 8000].
--bind=<bind> Interface to bind to [default: 127.0.0.1].
+ --xvfb Use a virtual X framebuffer for frontend testing, requires xvfbwrapper to be installed.
'''
def server(bind='127.0.0.1', port=8000, migrate=False):
-
if os.environ.get("RUN_MAIN") != "true":
from south.management.commands import syncdb, migrate
if migrate:
@@ -167,33 +167,51 @@ def shell():
with temp_dir() as STATIC_ROOT:
with temp_dir() as MEDIA_ROOT:
use_tz = VERSION[:2] >= (1, 4)
- configure(db_url=db_url,
- ROOT_URLCONF='cms.test_utils.project.urls',
- STATIC_ROOT=STATIC_ROOT,
- MEDIA_ROOT=MEDIA_ROOT,
- USE_TZ=use_tz,
- SOUTH_TESTS_MIGRATE=migrate
- )
+ configs = {
+ 'db_url': db_url,
+ 'ROOT_URLCONF': 'cms.test_utils.project.urls',
+ 'STATIC_ROOT': STATIC_ROOT,
+ 'MEDIA_ROOT': MEDIA_ROOT,
+ 'USE_TZ': use_tz,
+ 'SOUTH_TESTS_MIGRATE': migrate,
+ }
+ if args['test']:
+ configs['SESSION_ENGINE'] = "django.contrib.sessions.backends.cache"
+ configure(**configs)
# run
if args['test']:
- os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8082,8090-8100,9000-9200,7041'
-
- if args['isolated']:
- failures = isolated(args['<test-label>'], args['--parallel'])
- print()
- print("Failed tests")
- print("============")
- if failures:
- for failure in failures:
- print(" - %s" % failure)
- else:
- print(" None")
- num_failures = len(failures)
- elif args['timed']:
- num_failures = timed(args['<test-label>'])
+ # make "Address already in use" errors less likely, see Django
+ # docs for more details on this env variable.
+ os.environ.setdefault(
+ 'DJANGO_LIVE_TEST_SERVER_ADDRESS',
+ 'localhost:8000-9000'
+ )
+ if args['--xvfb']:
+ import xvfbwrapper
+ context = xvfbwrapper.Xvfb(width=1280, height=720)
else:
- num_failures = test(args['<test-label>'], args['--parallel'], args['--failfast'])
- sys.exit(num_failures)
+ @contextlib.contextmanager
+ def null_context():
+ yield
+ context = null_context()
+
+ with context:
+ if args['isolated']:
+ failures = isolated(args['<test-label>'], args['--parallel'])
+ print()
+ print("Failed tests")
+ print("============")
+ if failures:
+ for failure in failures:
+ print(" - %s" % failure)
+ else:
+ print(" None")
+ num_failures = len(failures)
+ elif args['timed']:
+ num_failures = timed(args['<test-label>'])
+ else:
+ num_failures = test(args['<test-label>'], args['--parallel'], args['--failfast'])
+ sys.exit(num_failures)
elif args['server']:
server(args['--bind'], args['--port'], migrate)
elif args['shell']:
View
20 docs/extending_cms/api_references.rst
@@ -433,11 +433,6 @@ cms.toolbar.items
``extra_classes`` should be either ``None`` or a list of class names as
strings.
- .. attribute:: URL_CHANGE
-
- Constant to be used with ``close_on_url`` to automatically close when
- the URL of the frame changes.
-
.. attribute:: REFRESH_PAGE
Constant to be used with ``on_close`` to refresh the current page when
@@ -480,26 +475,19 @@ cms.toolbar.items
Since positional insertion allows ``None``, it's safe to use the return
value of this method as the position argument to insertion APIs.
- .. method:: add_sideframe_item(name, url, active=False, disabled=False, extra_classes=None, close_on_url=None, on_close=None, side=LEFT, position=None)
+ .. method:: add_sideframe_item(name, url, active=False, disabled=False, extra_classes=None, on_close=None, side=LEFT, position=None)
Adds an item which opens ``url`` in the side frame and returns it.
- If ``close_on_url`` is set to :attr:`URL_CHANGE` the side frame will close
- once the URL in the side frame changes. If it's set to a string, it will
- close when the side frame URL is the same as the specified string.
-
``on_close`` can be set to ``None`` to do nothing when the side frame
closes, :attr:`REFRESH_PAGE` to refresh the page when it
closes or a URL to open once it closes.
- .. method:: add_modal_item(name, url, active=False, disabled=False, extra_classes=None, close_on_url=False, on_close=None, side=LEFT, position=None)
+ .. method:: add_modal_item(name, url, active=False, disabled=False, extra_classes=None, on_close=None, side=LEFT, position=None)
The same as :meth:`add_sideframe_item`, but opens the ``url`` in a
modal dialog instead of the side frame.
- Note that the default values for ``close_on_url`` and
- ``on_close`` differ from :meth:`add_sideframe_item`.
-
.. method:: add_link_item(name, url, active=False, disabled=False, extra_classes=None, side=LEFT, position=None)
Adds an item that simply opens ``url`` and returns it.
@@ -567,7 +555,7 @@ cms.toolbar.items