Skip to content

Commit

Permalink
Integrated Placeholder source field (#6496)
Browse files Browse the repository at this point in the history
  • Loading branch information
malindap92 authored and czpython committed Sep 3, 2018
1 parent 39562ae commit b075f44
Show file tree
Hide file tree
Showing 31 changed files with 442 additions and 57 deletions.
13 changes: 11 additions & 2 deletions cms/admin/pageadmin.py
Expand Up @@ -489,7 +489,12 @@ def delete_model(self, request, obj):
cms_pages.extend(self.model.objects.filter(node__in=nodes))

# Delete all of the pages titles contents
placeholders = Placeholder.objects.filter(pagecontent__page__in=cms_pages)
ct_page_content = ContentType.objects.get_for_model(PageContent)
page_content_objs = PageContent.objects.filter(page__in=cms_pages)
placeholders = Placeholder.objects.filter(
content_type=ct_page_content,
object_id__in=page_content_objs,
)
plugins = CMSPlugin.objects.filter(placeholder__in=placeholders)
QuerySet.delete(plugins)
placeholders.delete()
Expand Down Expand Up @@ -1046,7 +1051,11 @@ def delete_translation(self, request, object_id, extra_context=None):

titleopts = PageContent._meta
app_label = titleopts.app_label
saved_plugins = CMSPlugin.objects.filter(placeholder__pagecontent=translation, language=language)
placeholders = Placeholder.objects.get_for_obj(translation)
saved_plugins = CMSPlugin.objects.filter(
placeholder__in=placeholders,
language=language,
)
using = router.db_for_read(self.model)

if DJANGO_2_0:
Expand Down
4 changes: 3 additions & 1 deletion cms/forms/utils.py
Expand Up @@ -10,7 +10,7 @@
from cms.cache.choices import (
clean_site_choices_cache, clean_page_choices_cache,
_site_cache_key, _page_cache_key)
from cms.models import Page, PageContent
from cms.models import Page
from cms.utils import i18n


Expand All @@ -26,6 +26,8 @@ def get_sites():


def get_page_choices_for_site(site, language):
from cms.models import PageContent

fallbacks = i18n.get_fallback_languages(language, site_id=site.pk)
languages = [language] + fallbacks
translation_lookup = Prefetch(
Expand Down
57 changes: 57 additions & 0 deletions cms/migrations/0033_placeholder_source_data_migration.py
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-08-24 08:22
from __future__ import unicode_literals

from django.db import migrations


def forwards(apps, schema_editor):
db_alias = schema_editor.connection.alias
ContentType = apps.get_model('contenttypes', 'ContentType')
CMSPlugin = apps.get_model('cms', 'CMSPlugin')
Placeholder = apps.get_model('cms', 'Placeholder')

for app in apps.get_app_configs():
for model_class in app.get_models():
if issubclass(model_class, (CMSPlugin, Placeholder)):
# We ignore all models inherited from CMSPlugin or Placeholder.
continue

# Get all fields which has a relationship to Placeholder.
pl_related_fields = [
f for f in model_class._meta.get_fields()
if f.related_model == Placeholder and not f.auto_created
]

if not pl_related_fields:
# Model has no related fields with Placeholder.
continue

# Model has related field/s with Placeholder.
# Now get all the objects for the model and populate source field.
cur_ct_obj = ContentType.objects.get_for_model(model_class)

for obj in model_class.objects.using(db_alias):
for pl_field in pl_related_fields:
obj_placeholder_field = getattr(obj, pl_field.name)

if pl_field.many_to_many:
obj_placeholder_field.update(
content_type_id=cur_ct_obj.pk,
object_id=obj.pk,
)
else:
obj_placeholder_field.content_type_id = cur_ct_obj.pk,
obj_placeholder_field.object_id = obj.pk
obj.save()


class Migration(migrations.Migration):

dependencies = [
('cms', '0032_remove_title_to_pagecontent'),
]

operations = [
migrations.RunPython(forwards),
]
19 changes: 19 additions & 0 deletions cms/migrations/0034_remove_pagecontent_placeholders.py
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-08-30 15:49
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('cms', '0033_placeholder_source_data_migration'),
]

operations = [
migrations.RemoveField(
model_name='pagecontent',
name='placeholders',
),
]
17 changes: 16 additions & 1 deletion cms/models/fields.py
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models

from cms.forms.fields import PageSelectFormField
from cms.models.placeholdermodel import Placeholder
from django.db import models


class PlaceholderField(models.ForeignKey):
Expand Down Expand Up @@ -92,3 +94,16 @@ def formfield(self, **kwargs):
}
defaults.update(kwargs)
return super(PageField, self).formfield(**defaults)


class PlaceholderRelationField(GenericRelation):

def __init__(self, **kwargs):
kwargs.pop('object_id_field', None)
kwargs.pop('content_type_field', None)
super(PlaceholderRelationField, self).__init__(
Placeholder,
object_id_field='object_id',
content_type_field='content_type',
**kwargs
)
11 changes: 11 additions & 0 deletions cms/models/managers.py
Expand Up @@ -2,6 +2,7 @@
import functools
import operator

from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.db import models
from django.db.models import Q
Expand Down Expand Up @@ -121,6 +122,16 @@ def get_title(self, page, language, language_fallback=False):
return None


class PlaceholderManager(models.Manager):

def get_for_obj(self, obj):
"""
Get all placeholders for given object
"""
content_type = ContentType.objects.get_for_model(obj)
return self.filter(content_type=content_type, object_id=obj.pk)


################################################################################
# Permissions
################################################################################
Expand Down
5 changes: 3 additions & 2 deletions cms/models/pagemodel.py
Expand Up @@ -873,9 +873,10 @@ def get_menu_title(self, language=None, fallback=True, force_reload=False):
return menu_title

def get_placeholders(self, language):
from cms.models import Placeholder
from cms.models import PageContent, Placeholder

return Placeholder.objects.filter(pagecontent__language=language, pagecontent__page=self)
page_content = PageContent.objects.get(language=language, page=self)
return Placeholder.objects.get_for_obj(page_content)

def get_changed_date(self, language=None, fallback=True, force_reload=False):
"""
Expand Down
28 changes: 14 additions & 14 deletions cms/models/placeholdermodel.py
Expand Up @@ -15,6 +15,7 @@
from cms.cache import invalidate_cms_page_cache
from cms.cache.placeholder import clear_placeholder_cache
from cms.exceptions import LanguageError
from cms.models.managers import PlaceholderManager
from cms.utils import get_site_id
from cms.utils.i18n import get_language_object
from cms.constants import (
Expand Down Expand Up @@ -47,6 +48,8 @@ class Placeholder(models.Model):
is_static = False
is_editable = True

objects = PlaceholderManager()

class Meta:
app_label = 'cms'
permissions = (
Expand Down Expand Up @@ -220,19 +223,8 @@ def _get_attached_field(self):
return None

def _get_attached_model(self):
if hasattr(self, '_attached_model_cache'):
return self._attached_model_cache

if self.page or self.pagecontent_set.exists():
from cms.models import Page
self._attached_model_cache = Page
return Page

field = self._get_attached_field()
if field:
self._attached_model_cache = field.model
return field.model
self._attached_model_cache = None
if self.source:
return self.source._meta.model
return None

def _get_attached_models(self):
Expand All @@ -241,16 +233,24 @@ def _get_attached_models(self):
"""
if hasattr(self, '_attached_models_cache'):
return self._attached_models_cache

self._attached_models_cache = [field.model for field in self._get_attached_fields()]

if self.source:
self._attached_models_cache += [self.source._meta.model]
return self._attached_models_cache

def _get_attached_objects(self):
"""
Returns a list of objects attached to this placeholder.
"""
return [obj for field in self._get_attached_fields()
objs = [obj for field in self._get_attached_fields()
for obj in getattr(self, field.remote_field.get_accessor_name()).all()]

if not objs and self.source:
return [self.source]
return objs

def page_getter(self):
if not hasattr(self, '_page'):
from cms.models.pagemodel import Page
Expand Down
17 changes: 4 additions & 13 deletions cms/models/titlemodels.py
@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from collections import OrderedDict

from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from cms import constants
from cms.models.fields import PlaceholderRelationField
from cms.models.managers import PageContentManager
from cms.models.pagemodel import Page
from cms.utils.conf import get_cms_setting
Expand Down Expand Up @@ -51,7 +50,7 @@ class PageContent(models.Model):
page = models.ForeignKey(Page, on_delete=models.CASCADE, verbose_name=_("page"), related_name="pagecontent_set")
creation_date = models.DateTimeField(_("creation date"), editable=False, default=timezone.now)
# Placeholders (plugins)
placeholders = models.ManyToManyField('cms.Placeholder', editable=False)
placeholders = PlaceholderRelationField()

created_by = models.CharField(
_("created by"), max_length=constants.PAGE_USERNAME_MAX_LENGTH,
Expand Down Expand Up @@ -108,17 +107,9 @@ def rescan_placeholders(self):
"""
Rescan and if necessary create placeholders in the current template.
"""
existing = OrderedDict()
placeholders = [pl.slot for pl in self.page.get_declared_placeholders()]

for placeholder in self.placeholders.all():
if placeholder.slot in placeholders:
existing[placeholder.slot] = placeholder
from cms.utils.placeholder import rescan_placeholders_for_obj

for placeholder in placeholders:
if placeholder not in existing:
existing[placeholder] = self.placeholders.create(slot=placeholder)
return existing
return rescan_placeholders_for_obj(self)

def get_placeholders(self):
if not hasattr(self, '_placeholder_cache'):
Expand Down
35 changes: 34 additions & 1 deletion cms/plugin_rendering.py
Expand Up @@ -23,7 +23,11 @@
from cms.utils.compat import DJANGO_1_11
from cms.utils.conf import get_cms_setting
from cms.utils.permissions import has_plugin_permission
from cms.utils.placeholder import get_toolbar_plugin_struct, restore_sekizai_context
from cms.utils.placeholder import (
get_toolbar_plugin_struct,
rescan_placeholders_for_obj,
restore_sekizai_context,
)
from cms.utils.plugins import get_plugin_restrictions


Expand Down Expand Up @@ -316,6 +320,35 @@ def get_editable_placeholder_context(self, placeholder, page=None):
}
return context

def render_obj_placeholder(self, slot, context, inherit,
nodelist=None, editable=True):
from cms.models import Placeholder

# Check if page, if so delegate to render_page_placeholder
if self.current_page:
return self.render_page_placeholder(
slot,
context,
inherit,
nodelist=nodelist,
editable=editable,
)

# Not page, therefore we will use toolbar object as
# the current object and render the placeholder
current_obj = self.toolbar.get_object()
rescan_placeholders_for_obj(current_obj)
placeholder = Placeholder.objects.get_for_obj(current_obj).get(slot=slot)
content = self.render_placeholder(
placeholder,
context=context,
page=current_obj,
editable=editable,
use_cache=True,
nodelist=None,
)
return content

def render_page_placeholder(self, slot, context, inherit,
page=None, nodelist=None, editable=True):
if not self.current_page:
Expand Down
19 changes: 11 additions & 8 deletions cms/templatetags/cms_js_tags.py
Expand Up @@ -3,11 +3,14 @@

import json

from classytags.core import Tag, Options
from cms.utils.encoder import SafeJSONEncoder
from django import template
from django.utils.safestring import mark_safe

from cms.utils.encoder import SafeJSONEncoder
from cms.utils.placeholder import get_declared_placeholders_for_obj, rescan_placeholders_for_obj

from classytags.core import Tag, Options

from sekizai.helpers import get_varname


Expand All @@ -32,15 +35,15 @@ def bool(value):

@register.simple_tag()
def render_cms_structure_js(renderer, obj):
cms_page = obj.page
markup_bits = []
page_placeholders_by_slot = obj.rescan_placeholders()
obj_placeholders_by_slot = rescan_placeholders_for_obj(obj)
declared_placeholders = get_declared_placeholders_for_obj(obj)

for placeholder_node in cms_page.get_declared_placeholders():
page_placeholder = page_placeholders_by_slot.get(placeholder_node.slot)
for placeholder_node in declared_placeholders:
obj_placeholder = obj_placeholders_by_slot.get(placeholder_node.slot)

if page_placeholder:
placeholder_js = renderer.render_page_placeholder(cms_page, page_placeholder)
if obj_placeholder:
placeholder_js = renderer.render_placeholder(obj_placeholder, language=None, page=obj)
markup_bits.append(placeholder_js)

return mark_safe('\n'.join(markup_bits))
Expand Down
2 changes: 1 addition & 1 deletion cms/templatetags/cms_tags.py
Expand Up @@ -298,7 +298,7 @@ def render_tag(self, context, name, extra_bits, nodelist=None):
inherit = 'inherit' in extra_bits

try:
content = renderer.render_page_placeholder(
content = renderer.render_obj_placeholder(
slot=name,
context=context,
inherit=inherit,
Expand Down
Empty file.

0 comments on commit b075f44

Please sign in to comment.