dynamicweb/cms/admin/settingsadmin.py

156 lines
6 KiB
Python

import copy
import json
from functools import update_wrapper
from urllib.parse import urlparse
from django.contrib import admin
from django.db import transaction
from django.http import (
HttpResponse,
HttpResponseBadRequest,
HttpResponseRedirect,
)
from django.http.request import QueryDict
from django.urls import Resolver404, path, re_path, resolve
from django.utils.decorators import method_decorator
from django.utils.html import conditional_escape
from django.utils.translation import override
from django.views.decorators.csrf import csrf_protect
from cms.admin.forms import RequestToolbarForm
from cms.models import UserSettings
from cms.models.contentmodels import PageContent
from cms.utils.page import get_page_from_request
from cms.utils.page_permissions import user_can_view_page
from cms.utils.permissions import user_can_view_placeholder_source
from cms.utils.urlutils import admin_reverse
@admin.register(UserSettings)
class SettingsAdmin(admin.ModelAdmin):
def get_urls(self):
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
return [
path('session_store/', self.session_store, name='%s_%s_session_store' % info),
path('cms-toolbar/', wrap(self.get_toolbar), name='%s_%s_get_toolbar' % info),
path('', wrap(self.change_view), name='%s_%s_change' % info),
re_path(r'^(.+)/$', wrap(self.change_view), name='%s_%s_change' % info),
]
@method_decorator(csrf_protect)
@transaction.atomic
def change_view(self, request, id=None):
model = self.model
try:
obj = model.objects.get(user=request.user)
except model.DoesNotExist:
return self.add_view(request)
return super().change_view(request, str(obj.pk))
def session_store(self, request):
"""
either POST or GET
POST should have a settings parameter
"""
if not request.user.is_staff:
return HttpResponse(json.dumps(""),
content_type="application/json")
if request.method == "POST":
request.session['cms_settings'] = conditional_escape(request.POST['settings'])
request.session.save()
return HttpResponse(
json.dumps(request.session.get('cms_settings', '')),
content_type="application/json"
)
def get_toolbar(self, request):
from cms.toolbar.toolbar import CMSToolbar
form = RequestToolbarForm(request.GET or None)
if not form.is_valid():
return HttpResponseBadRequest('Invalid parameters')
form_data = form.cleaned_data
cms_path = form_data.get('cms_path') or request.path_info
origin_url = urlparse(cms_path)
attached_obj = form_data.get('attached_obj')
if isinstance(attached_obj, PageContent):
current_page = attached_obj.page
else:
current_page = get_page_from_request(request, use_path=origin_url.path, clean_path=True)
# The object id/type come straight from the client, so enforce an
# object-level view check before rendering a toolbar for it. Without
# this, any staff user could read the title/breadcrumb/edit URLs of
# pages on other sites or of view-restricted pages they cannot access.
# Deny with the same generic response as an unknown object so this does
# not become an existence oracle.
if isinstance(attached_obj, PageContent):
if not user_can_view_page(request.user, current_page):
return HttpResponseBadRequest('Invalid parameters')
elif attached_obj is not None:
if not user_can_view_placeholder_source(request.user, attached_obj):
return HttpResponseBadRequest('Invalid parameters')
elif current_page is not None and not user_can_view_page(request.user, current_page):
return HttpResponseBadRequest('Invalid parameters')
data = QueryDict(query_string=origin_url.query, mutable=True)
placeholders = request.GET.getlist("placeholders[]")
if placeholders:
data.setlist('placeholders[]', placeholders)
request = copy.copy(request)
request.GET = data
request.current_page = current_page
try:
request.resolver_match = resolve(origin_url.path)
except Resolver404:
pass
request.toolbar = CMSToolbar(request, request_path=origin_url.path, _async=True)
request.toolbar.set_object(attached_obj or current_page)
return HttpResponse(request.toolbar.render())
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
def response_post_save_change(self, request, obj):
#
# When the user changes his language setting, we need to do two things:
# 1. Change the language-prefix for the sideframed admin view
# 2. Reload the whole window so that the new language affects the
# toolbar, etc.
#
# To do this, we first redirect the sideframe to the correct new, URL,
# but we pass a GET param 'reload_window', which instructs JS on that
# page to strip (to avoid infinite redirection loops) that param then
# reload the whole window again.
#
with override(obj.language):
post_url = admin_reverse(
'cms_usersettings_change',
args=[obj.id, ],
current_app=self.admin_site.name
)
return HttpResponseRedirect(f"{post_url}?reload_window")
def has_change_permission(self, request, obj=None):
if obj and obj.user == request.user:
return True
return False
def get_model_perms(self, request):
"""
Return empty perms dict thus hiding the model from admin index.
"""
return {}