Skip to content

Commit

Permalink
✨(plugins) added Slider plugin
Browse files Browse the repository at this point in the history
This won't make it because djangocms-text-ckeditor has
a bug with inline forms, newly added item does not properly
initialize the CKEditor Javascript

django-cms/djangocms-text-ckeditor#680

So finally  we need to make slide item through children plugin
of SliderPlugin.
  • Loading branch information
sveetch committed Nov 21, 2024
1 parent e6aca9e commit 65d1b09
Show file tree
Hide file tree
Showing 12 changed files with 439 additions and 1 deletion.
1 change: 1 addition & 0 deletions sandbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ class Base(StyleguideMixin, DRFMixin, RichieCoursesConfigurationMixin, Configura
"richie.plugins.section",
"richie.plugins.simple_picture",
"richie.plugins.simple_text_ckeditor",
"richie.plugins.slider",
"richie.plugins.lti_consumer",
"richie",
# Third party apps
Expand Down
13 changes: 12 additions & 1 deletion src/richie/apps/courses/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def richie_placeholder_conf(name):
# Homepage
"richie/homepage.html maincontent": {
"name": _("Main content"),
"plugins": ["LargeBannerPlugin", "SectionPlugin"],
"plugins": ["LargeBannerPlugin", "SectionPlugin", "SliderPlugin"],
"child_classes": {
"SectionPlugin": [
"BlogPostPlugin",
Expand Down Expand Up @@ -578,3 +578,14 @@ def richie_placeholder_conf(name):
"sizes": "60px",
},
}

# If true the toolbar item will already be showed. If false only a page which already
# have the extension will have the toolbar item and users won't be able to add
# MainMenuEntry extension on existing page, only create new page with index extension
# through the wizard.
RICHIE_MAINMENUENTRY_ALLOW_CREATION = False

# Define which node level can be processed to search for MainMenuEntry extension. You
# can set it to 'None' for never processing any node.
# This is a limit against performance issues to avoid making querysets for nothing.
RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL = 0
Empty file.
37 changes: 37 additions & 0 deletions src/richie/plugins/slider/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django.contrib import admin
from django.utils.translation import gettext_lazy as _

from .models import SlideItem
from .forms import SlideItemForm


class SlideItemAdmin(admin.StackedInline):
"""
Plugin admin form to enable inline mode inside SliderPlugin
"""
model = SlideItem
form = SlideItemForm
extra = 0
verbose_name = _("Slide")
ordering = ["order"]
fieldsets = [
(None, {
"fields": (
"slider",
),
}),
(_("Content"), {
"fields": (
"title",
"image",
"link_url",
"content",
),
}),
(_("Options"), {
"fields": (
"order",
"link_open_blank",
),
}),
]
39 changes: 39 additions & 0 deletions src/richie/plugins/slider/cms_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
Slider CMS plugin
"""

from django.utils.translation import gettext_lazy as _

from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool

from richie.apps.core.defaults import PLUGINS_GROUP

from .admin import SlideItemAdmin
from .forms import SliderForm
from .models import Slider


@plugin_pool.register_plugin
class SliderPlugin(CMSPluginBase):
"""
Slider interface is able to add/edit/remove slide items as inline forms.
"""
cache = True
module = PLUGINS_GROUP
name = _("Slider")
model = Slider
form = SliderForm
inlines = (SlideItemAdmin,)
render_template = "richie/slider/slider.html"
fieldsets = (
(None, {"fields": ["title"]}),
)

def render(self, context, instance, placeholder):
context.update({
"instance": instance,
"placeholder": placeholder,
"slides": instance.slide_item.all().order_by("order")
})
return context
41 changes: 41 additions & 0 deletions src/richie/plugins/slider/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
Slider CMS plugin factories
"""

import factory
import faker

from .models import Slider, SlideItem


class SliderFactory(factory.django.DjangoModelFactory):
"""
Factory to create instance of a Slider.
"""
title = factory.Faker("text", max_nb_chars=20)

class Meta:
model = Slider


class SlideItemFactory(factory.django.DjangoModelFactory):
"""
Factory to create instance of a SlideItem.
"""
slider = factory.SubFactory(SliderFactory)
title = factory.Faker("text", max_nb_chars=20)
content = factory.Faker("text", max_nb_chars=42)
order = factory.Sequence(lambda n: 10 * n)
image = factory.SubFactory("richie.apps.core.factories.FilerImageFactory")
link_open_blank = factory.Faker("pybool")

class Meta:
model = SlideItem

@factory.lazy_attribute
def link_url(self):
"""
Set a random url
"""
Faker = faker.Faker()
return Faker.url()
38 changes: 38 additions & 0 deletions src/richie/plugins/slider/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Slider plugin forms
"""

from django import forms

from djangocms_text_ckeditor.widgets import TextEditorWidget

from .models import Slider, SlideItem

CKEDITOR_CONFIGURATION_NAME = "CKEDITOR_SETTINGS"


class SliderForm(forms.ModelForm):
class Meta:
model = Slider
exclude = []
fields = [
"title",
]


class SlideItemForm(forms.ModelForm):
class Meta:
model = SlideItem
exclude = []
fields = [
"slider",
"title",
"order",
"image",
"content",
"link_url",
"link_open_blank",
]
widgets = {
"content": TextEditorWidget,
}
48 changes: 48 additions & 0 deletions src/richie/plugins/slider/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 4.2.16 on 2024-11-20 02:07

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import filer.fields.image


class Migration(migrations.Migration):

initial = True

dependencies = [
('cms', '0022_auto_20180620_1551'),
migrations.swappable_dependency(settings.FILER_IMAGE_MODEL),
]

operations = [
migrations.CreateModel(
name='Slider',
fields=[
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='%(app_label)s_%(class)s', serialize=False, to='cms.cmsplugin')),
('title', models.CharField(max_length=255, verbose_name='title')),
],
options={
'verbose_name': 'Slider',
'verbose_name_plural': 'Sliders',
},
bases=('cms.cmsplugin',),
),
migrations.CreateModel(
name='SlideItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(default='', max_length=150, verbose_name='title')),
('content', models.TextField(blank=True, default='', verbose_name='content')),
('order', models.IntegerField(default=0, verbose_name='order')),
('link_url', models.URLField(blank=True, help_text='Make the slide as a link with an URL.', max_length=255, null=True, verbose_name='link URL')),
('link_open_blank', models.BooleanField(default=False, help_text='If checked the link will be open in a new window', verbose_name='open new window')),
('image', filer.fields.image.FilerImageField(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='slide_image', to=settings.FILER_IMAGE_MODEL, verbose_name='image')),
('slider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slide_item', to='slider.slider')),
],
options={
'verbose_name': 'Slide item',
'verbose_name_plural': 'Slide items',
},
),
]
Empty file.
109 changes: 109 additions & 0 deletions src/richie/plugins/slider/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
Slider plugin models
"""

from django.conf import settings
from django.db import models
from django.utils.html import strip_tags
from django.utils.text import Truncator
from django.utils.translation import gettext_lazy as _

from cms.models.pluginmodel import CMSPlugin
from filer.fields.image import FilerImageField


class Slider(CMSPlugin):
"""
Slide container for items.
.. Note::
We did slide item with common foreign key for slides instead of by the way of
child plugins.
The first is more natural and efficient but the latter has natural drag-n-drop
ordering (in CMS content structure) and maybe less huge form (since slide form
is delegate to child plugin form instead of including slide items as inline
forms).
"""
title = models.CharField(_("title"), max_length=255)

def __str__(self):
return Truncator(self.title).words(6, truncate="...")

def copy_relations(self, oldinstance):
"""
Copy FK relations when plugin object is copied as another object
"""
super().copy_relations(oldinstance)

self.slide_item.all().delete()

for slide_item in oldinstance.slide_item.all():
slide_item.pk = None
slide_item.slider = self
slide_item.save()

class Meta:
verbose_name = _("Slider")
verbose_name_plural = _("Sliders")


class SlideItem(models.Model):
"""
Slide item to include in container.
.. Note::
Glimpse plugin has similar link feature but:
* It contains both external free link and CMS page link;
* It does not have option field to enable opening link a blank window (option
is enabled automatically for external links only);
Should we harmonize with Glimpse or does this implementation is better like
this?
"""
slider = models.ForeignKey(
Slider,
related_name="slide_item",
on_delete=models.CASCADE
)
title = models.CharField(
_("title"),
max_length=150,
default="",
)
image = FilerImageField(
related_name="slide_image",
verbose_name=_("image"),
on_delete=models.SET_NULL,
null=True,
default=None,
)
content = models.TextField(
_("content"),
blank=True,
default="",
)
order = models.IntegerField(
_("order"),
default=0
)
link_url = models.URLField(
verbose_name=_("link URL"),
blank=True,
null=True,
max_length=255,
help_text=_("Make the slide as a link with an URL."),
)
link_open_blank = models.BooleanField(
_("open new window"),
default=False,
help_text=_("If checked the link will be open in a new window"),
)

def __str__(self):
return Truncator(self.title).words(6, truncate="...")

class Meta:
verbose_name = _("Slide item")
verbose_name_plural = _("Slide items")
27 changes: 27 additions & 0 deletions src/richie/plugins/slider/templates/richie/slider/slider.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% load i18n thumbnail %}{% spaceless %}

<div class="slider">
<div class="slider__items" count="{{ slides }}">
{% for item in slides %}
<div class="slider-item"
style="background-image: url('{% thumbnail item.image 1400x800 replace_alpha='#FFFFFF' crop=',0' %}');">

<p class="slider-item__title">{{ item.title }}</p>

{% if item.content %}
<div class="slider-item__content">{{ item.content|safe }}</div>
{% endif %}

{% if item.link_url %}
<p class="slider-item__link">
<a href="{{ item.link_url }}"{% if item.link_open_blank %} target="_blank"{% endif %}>
{% if item.link_name %}{{ item.link_name }}{% else %}{% trans "Read more" %}{% endif %}
</a>
</p>
{% endif %}
</div>
{% endfor %}
</div>
</div>

{% endspaceless %}
Loading

0 comments on commit 65d1b09

Please sign in to comment.