Skip to content

Commit

Permalink
Add BlogFeaturedPostsPlugin to show selected posts (#747)
Browse files Browse the repository at this point in the history
  • Loading branch information
protoroto committed Aug 7, 2023
1 parent c57ed71 commit 7f291f5
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 1 deletion.
1 change: 1 addition & 0 deletions changes/428.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add BlogFeaturedPostsPlugin to show selected posts
44 changes: 43 additions & 1 deletion djangocms_blog/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.template.loader import select_template

from .forms import AuthorPostsForm, BlogPluginForm, LatestEntriesForm
from .models import AuthorEntriesPlugin, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, Post
from .models import AuthorEntriesPlugin, BlogCategory, FeaturedPostsPlugin, GenericBlogPlugin, LatestPostsPlugin, Post
from .settings import get_setting


Expand Down Expand Up @@ -75,6 +75,48 @@ class BlogLatestEntriesPluginCached(BlogLatestEntriesPlugin):
cache = True


@plugin_pool.register_plugin
class BlogFeaturedPostsPlugin(BlogPlugin):
"""
Return the selected posts which bypasses cache.
"""

name = get_setting("FEATURED_POSTS_PLUGIN_NAME")
model = FeaturedPostsPlugin
form = BlogPluginForm
cache = False
base_render_template = "featured_posts.html"

def get_fields(self, request, obj=None):
"""
Return the fields available when editing the plugin.
'template_folder' field is added if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains multiple folders.
"""
fields = ["app_config", "posts"]
if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1:
fields.append("template_folder")
return fields

def render(self, context, instance, placeholder):
"""Render the plugin."""
context = super().render(context, instance, placeholder)
context["posts_list"] = instance.get_posts(context["request"])
context["TRUNCWORDS_COUNT"] = get_setting("POSTS_LIST_TRUNCWORDS_COUNT")
return context


@plugin_pool.register_plugin
class BlogFeaturedPostsPluginCached(BlogFeaturedPostsPlugin):
"""
Return the selected posts caching the result.
"""

name = get_setting("FEATURED_POSTS_PLUGIN_NAME_CACHED")
cache = True


@plugin_pool.register_plugin
class BlogAuthorPostsPlugin(BlogPlugin):
"""Render the list of authors."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Generated by Django 4.2.3 on 2023-08-04 09:10

import aldryn_apphooks_config.fields
import django.db.models.deletion
import sortedm2m.fields
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("cms", "0022_auto_20180620_1551"),
("djangocms_blog", "0041_auto_20230720_1508"),
]

operations = [
migrations.AlterField(
model_name="authorentriesplugin",
name="cmsplugin_ptr",
field=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",
),
),
migrations.AlterField(
model_name="genericblogplugin",
name="cmsplugin_ptr",
field=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",
),
),
migrations.AlterField(
model_name="latestpostsplugin",
name="cmsplugin_ptr",
field=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",
),
),
migrations.CreateModel(
name="FeaturedPostsPlugin",
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",
),
),
(
"current_site",
models.BooleanField(
default=True, help_text="Select items from the current site only", verbose_name="current site"
),
),
(
"template_folder",
models.CharField(
choices=[("plugins", "Default template")],
default="plugins",
help_text="Select plugin template to load for this instance",
max_length=200,
verbose_name="Plugin template",
),
),
(
"app_config",
aldryn_apphooks_config.fields.AppHookConfigField(
blank=True,
help_text="When selecting a value, the form is reloaded to get the updated default",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="djangocms_blog.blogconfig",
verbose_name="app. config",
),
),
(
"posts",
sortedm2m.fields.SortedManyToManyField(
help_text=None, to="djangocms_blog.post", verbose_name="Featured posts"
),
),
],
options={
"abstract": False,
},
bases=("cms.cmsplugin",),
),
]
14 changes: 14 additions & 0 deletions djangocms_blog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,20 @@ def get_authors(self, request):
return authors


class FeaturedPostsPlugin(BasePostPlugin):
posts = SortedManyToManyField(Post, verbose_name=_("Featured posts"))

def __str__(self):
return _("Featured posts")

def copy_relations(self, oldinstance):
self.posts.set(oldinstance.posts.all())

def get_posts(self, request, published_only=True):
posts = self.post_queryset(request, published_only)
return posts


class GenericBlogPlugin(BasePostPlugin):
class Meta:
abstract = False
Expand Down
14 changes: 14 additions & 0 deletions djangocms_blog/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,20 @@
Name of the plugin showing the blog archive index.
"""

BLOG_FEATURED_POSTS_PLUGIN_NAME = _("Featured Posts")
"""
.. _FEATURED_POSTS_PLUGIN_NAME:
Name of the plugin showing the selected posts.
"""

BLOG_FEATURED_POSTS_PLUGIN_NAME_CACHED = _("Featured Posts - Cache")
"""
.. _FEATURED_POSTS_PLUGIN_NAME_CACHED:
Name of the plugin showing the selected posts (cached version).
"""

BLOG_FEED_CACHE_TIMEOUT = 3600
"""
.. _FEED_CACHE_TIMEOUT:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% load i18n %}{% spaceless %}
<div class="plugin plugin-blog">
<div class="blog-featured-posts">
{% for post in posts_list %}
{% include "djangocms_blog/includes/blog_item.html" with post=post image="true" TRUNCWORDS_COUNT=TRUNCWORDS_COUNT %}
{% endfor %}
</div>
</div>
{% endspaceless %}
27 changes: 27 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,30 @@ def test_plugin_author(self):
self.assertEqual(len(plugin.get_posts(request)), 2)
self.assertEqual(plugin.get_authors(request)[0].count, 2)

def test_plugin_featured_posts(self):
post1 = self._get_post(self._post_data[0]["en"])
post1.publish = True
post1.save()
post2 = self._get_post(self._post_data[1]["en"])
request = self.get_page_request("/", AnonymousUser(), r"/en/blog/", edit=False)
plugin = add_plugin(post1.content, "BlogFeaturedPostsPlugin", language="en", app_config=self.app_config_1)
plugin.posts.add(post1, post2)
self.assertEqual(len(plugin.get_posts(request)), 1)

post2.publish = True
post2.save()
self.assertEqual(len(plugin.get_posts(request)), 2)

def test_copy_plugin_featured_post(self):
post1 = self._get_post(self._post_data[0]["en"])
post2 = self._get_post(self._post_data[1]["en"])
plugin = add_plugin(post1.content, "BlogFeaturedPostsPlugin", language="en", app_config=self.app_config_1)
plugin.posts.add(post1, post2)
plugins = list(post1.content.cmsplugin_set.filter(language="en").order_by("path", "depth", "position"))
copy_plugins_to(plugins, post2.content)
new = list(downcast_plugins(post2.content.cmsplugin_set.all()))
self.assertEqual(set(new[0].posts.all()), {post1, post2})

def test_copy_plugin_author(self):
post1 = self._get_post(self._post_data[0]["en"])
post2 = self._get_post(self._post_data[1]["en"])
Expand Down Expand Up @@ -1402,6 +1426,9 @@ def test_str_repr(self):
plugin = add_plugin(post1.content, "BlogArchivePlugin", language="en", app_config=self.app_config_1)
self.assertEqual(force_str(plugin.__str__()), "generic blog plugin")

plugin = add_plugin(post1.content, "BlogFeaturedPostsPlugin", language="en", app_config=self.app_config_1)
self.assertEqual(plugin.__str__(), "Featured posts")

# create fake empty post - assign a random pk to trick ORM / parler to think the object has been saved
# due to how safe_translation_getter works
no_translation_post = Post()
Expand Down
60 changes: 60 additions & 0 deletions tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,62 @@ def test_plugin_latest(self):
self.assertTrue(rendered.find('<article id="post-first-post"') > -1)
self.assertTrue(rendered.find('<article id="post-different-appconfig"') > -1)

def test_plugin_featured_cached(self):
pages = self.get_pages()
posts = self.get_posts()
ph = pages[0].placeholders.get(slot="content")

plugin = add_plugin(ph, "BlogFeaturedPostsPluginCached", language="en", app_config=self.app_config_1)
plugin.posts.add(posts[0])
rendered = self.render_plugin(pages[0], "en", plugin, edit=True)
try:
self.assertTrue(rendered.find("cms-plugin-djangocms_blog-post-abstract-%s" % posts[0].pk) > -1)
except AssertionError:
self.assertTrue(rendered.find("cms_plugin-djangocms_blog-post-abstract-%s" % posts[0].pk) > -1)
self.assertTrue(rendered.find("<p>first line</p>") > -1)
self.assertTrue(rendered.find('<article id="post-first-post"') > -1)
self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)

plugin_nocache = add_plugin(ph, "BlogFeaturedPostsPlugin", language="en", app_config=self.app_config_1)
plugin_nocache.posts.add(posts[0])
# FIXME: Investigate the correct number of queries expected here
with self.assertNumQueries(FuzzyInt(14, 15)):
self.render_plugin(pages[0], "en", plugin_nocache)

with self.assertNumQueries(FuzzyInt(14, 15)):
self.render_plugin(pages[0], "en", plugin)

with self.assertNumQueries(FuzzyInt(14, 15)):
rendered = self.render_plugin(pages[0], "en", plugin)

self.assertTrue(rendered.find("<p>first line</p>") > -1)
self.assertTrue(rendered.find('<article id="post-first-post"') > -1)
self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)

def test_plugin_featured(self):
pages = self.get_pages()
posts = self.get_posts()
posts[1].publish = True
posts[1].save()
ph = pages[0].placeholders.get(slot="content")

plugin = add_plugin(ph, "BlogFeaturedPostsPlugin", language="en", app_config=self.app_config_1)
plugin.posts.add(posts[0], posts[1])

rendered = self.render_plugin(pages[0], "en", plugin, edit=True)
try:
self.assertTrue(rendered.find("cms-plugin-djangocms_blog-post-abstract-%s" % posts[0].pk) > -1)
self.assertTrue(rendered.find("cms-plugin-djangocms_blog-post-abstract-%s" % posts[1].pk) > -1)
except AssertionError:
self.assertTrue(rendered.find("cms_plugin-djangocms_blog-post-abstract-%s" % posts[0].pk) > -1)
self.assertTrue(rendered.find("cms_plugin-djangocms_blog-post-abstract-%s" % posts[1].pk) > -1)
self.assertTrue(rendered.find("<p>first line</p>") > -1)
self.assertTrue(rendered.find("<p>second post first line</p>") > -1)
self.assertTrue(rendered.find('<article id="post-first-post"') > -1)
self.assertTrue(rendered.find('<article id="post-second-post"') > -1)
self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)
self.assertTrue(rendered.find(posts[1].get_absolute_url()) > -1)

def test_plugin_tags(self):
pages = self.get_pages()
posts = self.get_posts()
Expand Down Expand Up @@ -301,6 +357,8 @@ def test_plugin_templates_field_single_template(self):
"BlogTagsPlugin",
"BlogArchivePlugin",
"BlogCategoryPlugin",
"BlogFeaturedPostsPlugin",
"BlogFeaturedPostsPluginCached",
]
for plugin in plugins:
page_admin = admin.site._registry[Page]
Expand Down Expand Up @@ -333,6 +391,8 @@ def test_plugin_templates_field_multi_template(self):
"BlogTagsPlugin",
"BlogArchivePlugin",
"BlogCategoryPlugin",
"BlogFeaturedPostsPlugin",
"BlogFeaturedPostsPluginCached",
]
for plugin in plugins:
page_admin = admin.site._registry[Page]
Expand Down

0 comments on commit 7f291f5

Please sign in to comment.