瀏覽代碼

Merge branch 'dev' into release/2

Vince Salvino 1 年之前
父節點
當前提交
58e7c0c155
共有 39 個文件被更改,包括 763 次插入81 次删除
  1. 2 0
      coderedcms/blocks/__init__.py
  2. 14 1
      coderedcms/blocks/content_blocks.py
  3. 1 0
      coderedcms/blocks/html_blocks.py
  4. 113 0
      coderedcms/migrations/0036_filmstrip_filmpanel.py
  5. 40 0
      coderedcms/migrations/0037_coderedpage_related_classifier_term_and_more.py
  6. 116 0
      coderedcms/models/page_models.py
  7. 74 0
      coderedcms/models/snippet_models.py
  8. 4 6
      coderedcms/project_template/basic/website/migrations/0001_initial.py
  9. 4 6
      coderedcms/project_template/sass/website/migrations/0001_initial.py
  10. 2 0
      coderedcms/settings.py
  11. 17 0
      coderedcms/static/coderedcms/css/crx-front.css
  12. 1 0
      coderedcms/static/coderedcms/css/crx-front.css.map
  13. 0 0
      coderedcms/static/coderedcms/css/crx-front.min.css
  14. 32 1
      coderedcms/static/coderedcms/js/crx-front.js
  15. 15 0
      coderedcms/static/coderedcms/scss/_crx-filmstrip.scss
  16. 1 0
      coderedcms/static/coderedcms/scss/crx-front.scss
  17. 32 0
      coderedcms/templates/coderedcms/blocks/film_strip_block.html
  18. 7 9
      coderedcms/templates/coderedcms/blocks/pagelist_block.html
  19. 1 3
      coderedcms/templates/coderedcms/blocks/pagepreview_block.html
  20. 4 0
      coderedcms/templates/coderedcms/pages/article_page.html
  21. 29 8
      coderedcms/templates/coderedcms/pages/base.html
  22. 24 0
      coderedcms/templates/coderedcms/pages/form_page.mini.html
  23. 12 0
      coderedcms/templates/coderedcms/pages/page.mini.html
  24. 0 4
      coderedcms/templates/coderedcms/pages/search.html
  25. 0 16
      coderedcms/templates/coderedcms/pages/web_page_notitle.html
  26. 14 0
      docs/features/blocks/contentblocks/filmstrip.rst
  27. 2 2
      docs/features/blocks/contentblocks/index.rst
  28. 19 13
      docs/features/blocks/contentblocks/latestpages.rst
  29. 9 5
      docs/features/blocks/contentblocks/pagepreview.rst
  30. 二進制
      docs/features/img/related_pages.png
  31. 1 0
      docs/features/index.rst
  32. 63 0
      docs/features/related_pages.rst
  33. 38 0
      docs/features/snippets/filmstrip.rst
  34. 二進制
      docs/features/snippets/img/filmstrip_block.png
  35. 1 0
      docs/features/snippets/index.rst
  36. 1 1
      docs/how_to/docker.rst
  37. 15 4
      docs/releases/index.rst
  38. 2 2
      docs/releases/v2.0.0.rst
  39. 53 0
      docs/releases/v2.1.0.rst

+ 2 - 0
coderedcms/blocks/__init__.py

@@ -41,6 +41,7 @@ from .content_blocks import (  # noqa
     CardBlock,
     CarouselBlock,
     ContentWallBlock,
+    FilmStripBlock,
     ImageGalleryBlock,
     ModalBlock,
     NavDocumentLinkWithSubLinkBlock,
@@ -89,6 +90,7 @@ CONTENT_STREAMBLOCKS = HTML_STREAMBLOCKS + [
     ("accordion", AccordionBlock()),
     ("card", CardBlock()),
     ("carousel", CarouselBlock()),
+    ("film_strip", FilmStripBlock()),
     ("image_gallery", ImageGalleryBlock()),
     ("modal", ModalBlock(HTML_STREAMBLOCKS)),
     ("pricelist", PriceListBlock()),

+ 14 - 1
coderedcms/blocks/content_blocks.py

@@ -80,6 +80,19 @@ class CarouselBlock(BaseBlock):
         template = "coderedcms/blocks/carousel_block.html"
 
 
+class FilmStripBlock(BaseBlock):
+    """
+    Enables choosing a Film Strip Snippet.
+    """
+
+    film_strip = SnippetChooserBlock("coderedcms.FilmStrip")
+
+    class Meta:
+        icon = "image"
+        label = _("Film Strip")
+        template = "coderedcms/blocks/film_strip_block.html"
+
+
 class ImageGalleryBlock(BaseBlock):
     """
     Show a collection of images with interactive previews that expand to
@@ -204,7 +217,7 @@ class NavSubLinkBlock(BaseBlock):
 
 class NavExternalLinkWithSubLinkBlock(NavSubLinkBlock, NavExternalLinkBlock):
     """
-    Extermal link with option for sub-links.
+    External link with option for sub-links.
     """
 
     class Meta:

+ 1 - 0
coderedcms/blocks/html_blocks.py

@@ -228,6 +228,7 @@ class PageListBlock(BaseBlock):
         label=_("Classified as"),
         help_text=_("Only show pages that are classified with this term."),
     )
+    # DEPRECATED: Remove in 3.0
     show_preview = blocks.BooleanBlock(
         required=False,
         default=False,

+ 113 - 0
coderedcms/migrations/0036_filmstrip_filmpanel.py

@@ -0,0 +1,113 @@
+# Generated by Django 4.1.7 on 2023-03-30 19:14
+
+import coderedcms.fields
+from django.db import migrations, models
+import django.db.models.deletion
+import modelcluster.fields
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("wagtailimages", "0025_alter_image_file_alter_rendition_file"),
+        ("coderedcms", "0035_remove_googleapisettings_site_and_more"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="FilmStrip",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("name", models.CharField(max_length=255, verbose_name="Name")),
+            ],
+            options={
+                "verbose_name": "Film Strip",
+            },
+        ),
+        migrations.CreateModel(
+            name="FilmPanel",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "sort_order",
+                    models.IntegerField(blank=True, editable=False, null=True),
+                ),
+                (
+                    "background_color",
+                    models.CharField(
+                        blank=True,
+                        help_text="Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)",
+                        max_length=255,
+                        verbose_name="Background color",
+                    ),
+                ),
+                (
+                    "foreground_color",
+                    models.CharField(
+                        blank=True,
+                        help_text="Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)",
+                        max_length=255,
+                        verbose_name="Text color",
+                    ),
+                ),
+                (
+                    "custom_css_class",
+                    models.CharField(
+                        blank=True,
+                        max_length=255,
+                        verbose_name="Custom CSS class",
+                    ),
+                ),
+                (
+                    "custom_id",
+                    models.CharField(
+                        blank=True, max_length=255, verbose_name="Custom ID"
+                    ),
+                ),
+                (
+                    "content",
+                    coderedcms.fields.CoderedStreamField(
+                        blank=True, use_json_field=True
+                    ),
+                ),
+                (
+                    "background_image",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.SET_NULL,
+                        related_name="+",
+                        to="wagtailimages.image",
+                        verbose_name="Background image",
+                    ),
+                ),
+                (
+                    "film_strip",
+                    modelcluster.fields.ParentalKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="film_panels",
+                        to="coderedcms.filmstrip",
+                        verbose_name="Film Panel",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Film Panel",
+            },
+        ),
+    ]

+ 40 - 0
coderedcms/migrations/0037_coderedpage_related_classifier_term_and_more.py

@@ -0,0 +1,40 @@
+# Generated by Django 4.1.8 on 2023-04-13 21:36
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("coderedcms", "0036_filmstrip_filmpanel"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="coderedpage",
+            name="related_classifier_term",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="When getting related pages, pages with this term will be weighted over other classifier terms. By default, pages with the greatest number of classifiers in common are ranked highest.",
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="+",
+                to="coderedcms.classifierterm",
+                verbose_name="Preferred related classifier term",
+            ),
+        ),
+        migrations.AddField(
+            model_name="coderedpage",
+            name="related_num",
+            field=models.PositiveIntegerField(
+                default=3, verbose_name="Number of related pages to show"
+            ),
+        ),
+        migrations.AddField(
+            model_name="coderedpage",
+            name="related_show",
+            field=models.BooleanField(
+                default=False, verbose_name="Show list of related pages"
+            ),
+        ),
+    ]

+ 116 - 0
coderedcms/models/page_models.py

@@ -146,6 +146,12 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
     # ajax_template = ''
     # search_template = ''
 
+    # Template used in site search results.
+    search_template = "coderedcms/pages/search_result.html"
+
+    # Template used for related pages, Latest Pages block, and Page Preview block.
+    miniview_template = "coderedcms/pages/page.mini.html"
+
     ###############
     # Content fields
     ###############
@@ -217,6 +223,41 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
         help_text=_("Enable filtering child pages by these classifiers."),
     )
 
+    #####################
+    # Related Page Fields
+    #####################
+
+    # Subclasses can override this to query on a specific page model, in the
+    # format "appname.Model". By default sibling pages are used.
+    related_query_pagemodel = None
+
+    # Subclasses can override this to enabled related pages by default.
+    related_show_default = False
+
+    related_show = models.BooleanField(
+        default=related_show_default,
+        verbose_name=_("Show list of related pages"),
+    )
+
+    related_num = models.PositiveIntegerField(
+        default=3,
+        verbose_name=_("Number of related pages to show"),
+    )
+
+    related_classifier_term = models.ForeignKey(
+        "coderedcms.ClassifierTerm",
+        blank=True,
+        null=True,
+        on_delete=models.SET_NULL,
+        related_name="+",
+        verbose_name=_("Preferred related classifier term"),
+        help_text=_(
+            "When getting related pages, pages with this term will be "
+            "weighted over other classifier terms. By default, pages with "
+            "the greatest number of classifiers in common are ranked highest."
+        ),
+    )
+
     ###############
     # Layout fields
     ###############
@@ -311,6 +352,14 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
             ],
             heading=_("Show Child Pages"),
         ),
+        MultiFieldPanel(
+            [
+                FieldPanel("related_show"),
+                FieldPanel("related_num"),
+                FieldPanel("related_classifier_term"),
+            ],
+            heading=_("Related Pages"),
+        ),
     ]
 
     promote_panels = SeoMixin.seo_meta_panels + SeoMixin.seo_struct_panels
@@ -339,6 +388,7 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
         if not self.id:
             self.index_order_by = self.index_order_by_default
             self.index_show_subpages = self.index_show_subpages_default
+            self.related_show = self.related_show_default
 
     @cached_classmethod
     def get_edit_handler(cls):
@@ -455,6 +505,60 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
 
         return query
 
+    def get_related_pages(
+        self, pagetype: str = None, num: int = None
+    ) -> models.QuerySet:
+        """
+        Returns a queryset of sibling pages, or the model type
+        defined by ``pagetype`` or ``self.related_query_pagemodel``,
+        ordered by number of shared classifier terms.
+
+        :param str pagetype: The model type to query on. This should
+          be a string in the format "appname.Model".
+          Overrides ``self.related_query_pagemodel``
+
+        :param int num: The number of results to return.
+          Overrides ``self.related_num``.
+        """
+
+        if pagetype is None:
+            pagetype = self.related_query_pagemodel
+
+        if num is None:
+            num = self.related_num
+
+        # Get our related query model, and queryset.
+        if pagetype:
+            querymodel = resolve_model_string(pagetype, self._meta.app_label)
+            r_qs = querymodel.objects.all().live()
+        else:
+            r_qs = self.get_parent().specific.get_index_children()
+
+        # Exclude self to avoid infinite recursion.
+        r_qs = r_qs.exclude(pk=self.pk)
+
+        order_by = []
+
+        # If we have a preferred classifier term, order by that.
+        if self.related_classifier_term:
+            p_ct_q = models.Q(classifier_terms=self.related_classifier_term)
+            r_qs = r_qs.annotate(p_ct=p_ct_q)
+            order_by.append("-p_ct")
+
+        # If this page has a classifier, then order by number of
+        # shared classifier terms.
+        if self.classifier_terms.exists():
+            r_ct_q = models.Q(classifier_terms__in=self.classifier_terms.all())
+            r_qs = r_qs.annotate(r_ct=models.Count("classifier_terms", r_ct_q))
+            order_by.append("-r_ct")
+
+        # Order the related pages, then add distinct to deal with
+        # annotating based on a many to many.
+        if order_by:
+            r_qs = r_qs.order_by(*order_by).distinct()
+
+        return r_qs[:num]
+
     def get_content_walls(self, check_child_setting=True):
         current_content_walls = []
         if check_child_setting:
@@ -478,6 +582,7 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
         """
         context = super().get_context(request)
 
+        # Show list of child pages.
         if self.index_show_subpages:
             # Get child pages
             all_children = self.get_index_children()
@@ -528,9 +633,16 @@ class CoderedPage(WagtailCacheMixin, SeoMixin, Page, metaclass=CoderedPageMeta):
 
             context["index_paginated"] = paged_children
             context["index_children"] = all_children
+
+        # Show a list of related pages.
+        if self.related_show:
+            context["related_pages"] = self.get_related_pages()
+
+        # Content walls.
         context["content_walls"] = self.get_content_walls(
             check_child_setting=False
         )
+
         return context
 
 
@@ -593,6 +705,9 @@ class CoderedArticlePage(CoderedWebPage):
         abstract = True
 
     template = "coderedcms/pages/article_page.html"
+    search_template = "coderedcms/pages/article_page.search.html"
+
+    related_show_default = True
 
     # Override body to provide simpler content
     body = StreamField(
@@ -1593,6 +1708,7 @@ class CoderedFormPage(CoderedFormMixin, CoderedWebPage):
         abstract = True
 
     template = "coderedcms/pages/form_page.html"
+    miniview_template = "coderedcms/pages/form_page.mini.html"
     landing_page_template = "coderedcms/pages/form_page_landing.html"
 
     base_form_class = WagtailAdminFormPageForm

+ 74 - 0
coderedcms/models/snippet_models.py

@@ -216,6 +216,80 @@ class ClassifierTerm(Orderable, models.Model):
         return "{0} > {1}".format(self.classifier.name, self.name)
 
 
+@register_snippet
+class FilmStrip(ClusterableModel):
+    class Meta:
+        verbose_name = _("Film Strip")
+
+    name = models.CharField(
+        max_length=255,
+        verbose_name=_("Name"),
+    )
+
+    panels = [
+        FieldPanel("name"),
+        InlinePanel("film_panels", label=_("Panels")),
+    ]
+
+    def __str__(self):
+        return self.name
+
+
+class FilmPanel(Orderable, models.Model):
+    class Meta:
+        verbose_name = _("Film Panel")
+
+    film_strip = ParentalKey(
+        FilmStrip,
+        related_name="film_panels",
+        verbose_name=_("Film Panel"),
+    )
+    background_image = models.ForeignKey(
+        get_image_model_string(),
+        null=True,
+        blank=True,
+        on_delete=models.SET_NULL,
+        related_name="+",
+        verbose_name=_("Background image"),
+    )
+    background_color = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_("Background color"),
+        help_text=_("Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)"),
+    )
+    foreground_color = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_("Text color"),
+        help_text=_("Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)"),
+    )
+    custom_css_class = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_("Custom CSS class"),
+    )
+    custom_id = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_("Custom ID"),
+    )
+    content = CoderedStreamField(
+        HTML_STREAMBLOCKS,
+        blank=True,
+        use_json_field=True,
+    )
+
+    panels = [
+        FieldPanel("background_image"),
+        FieldPanel("background_color"),
+        FieldPanel("foreground_color"),
+        FieldPanel("custom_css_class"),
+        FieldPanel("custom_id"),
+        FieldPanel("content"),
+    ]
+
+
 @register_snippet
 class Navbar(models.Model):
     """

文件差異過大導致無法顯示
+ 4 - 6
coderedcms/project_template/basic/website/migrations/0001_initial.py


文件差異過大導致無法顯示
+ 4 - 6
coderedcms/project_template/sass/website/migrations/0001_initial.py


+ 2 - 0
coderedcms/settings.py

@@ -183,6 +183,7 @@ class _DefaultSettings:
                 "Card masonry - fluid brick pattern",
             ),
         ],
+        # DEPRECATED: Remove in 3.0.
         "pagelistblock": [
             (
                 "coderedcms/blocks/pagelist_block.html",
@@ -209,6 +210,7 @@ class _DefaultSettings:
                 "Article, card masonry - fluid brick pattern",
             ),
         ],
+        # DEPRECATED: Remove in 3.0
         "pagepreviewblock": [
             (
                 "coderedcms/blocks/pagepreview_card.html",

+ 17 - 0
coderedcms/static/coderedcms/css/crx-front.css

@@ -36,6 +36,23 @@ License: https://github.com/coderedcorp/coderedcms/blob/dev/LICENSE
   height: 500px;
 }
 
+.crx-filmstrip-container {
+  overflow: hidden;
+}
+
+@media (hover: none) {
+  .crx-filmstrip-container {
+    overflow-x: scroll;
+  }
+}
+
+.crx-filmstrip-panel {
+  min-width: 300px;
+  min-height: 300px;
+  padding: 40px;
+  text-align: center;
+}
+
 .modal-lightbox {
   max-width: 100vw;
   text-align: center;

文件差異過大導致無法顯示
+ 1 - 0
coderedcms/static/coderedcms/css/crx-front.css.map


文件差異過大導致無法顯示
+ 0 - 0
coderedcms/static/coderedcms/css/crx-front.min.css


+ 32 - 1
coderedcms/static/coderedcms/js/crx-front.js

@@ -132,6 +132,37 @@ document.addEventListener("DOMContentLoaded", function () {
   if (document.querySelectorAll("[data-masonry]").length > 0) {
     load_script(libs.masonry);
   }
-});
 
+  /** Film Strip Controls **/
+  let strips = document.querySelectorAll("[data-block='film-strip']");
+  strips.forEach((el) => {
+    const leftButton = el.querySelector("[data-button='left']");
+    const rightButton = el.querySelector("[data-button='right']");
+    const container = el.querySelector("[data-block='film-container']");
+
+    leftButton.addEventListener("click", function () {
+      const panels = el.querySelectorAll("[data-block='film-panel']");
+      let currentBlock = parseInt(el.dataset.currentBlock) - 1;
+      if (currentBlock < 0) currentBlock = panels.length - 1;
+      el.dataset.currentBlock = currentBlock;
+
+      const elem = panels[currentBlock];
+      const left = elem.offsetLeft;
+
+      container.scroll({ top: 0, left: left, behavior: "smooth" });
+    });
+
+    rightButton.addEventListener("click", function () {
+      const panels = el.querySelectorAll("[data-block='film-panel']");
+      let currentBlock = parseInt(el.dataset.currentBlock) + 1;
+      if (currentBlock >= panels.length) currentBlock = 0;
+      el.dataset.currentBlock = currentBlock;
+
+      const elem = panels[currentBlock];
+      const left = elem.offsetLeft;
+
+      container.scroll({ top: 0, left: left, behavior: "smooth" });
+    });
+  });
+});
 /* @license-end */

+ 15 - 0
coderedcms/static/coderedcms/scss/_crx-filmstrip.scss

@@ -0,0 +1,15 @@
+.crx-filmstrip-container {
+  overflow: hidden;
+  // If device does not support hover, it is most likely a touchscreen device.
+  // In that case, allow manual scrolling.
+  @media (hover: none) {
+    overflow-x: scroll;
+  }
+}
+
+.crx-filmstrip-panel {
+  min-width: 300px;
+  min-height: 300px;
+  padding: 40px;
+  text-align: center;
+}

+ 1 - 0
coderedcms/static/coderedcms/scss/crx-front.scss

@@ -9,6 +9,7 @@ License: https://github.com/coderedcorp/coderedcms/blob/dev/LICENSE
 
 @import "crx-article";
 @import "crx-bs-overrides";
+@import "crx-filmstrip";
 @import "crx-gallery";
 @import "crx-hero";
 @import "crx-location";

+ 32 - 0
coderedcms/templates/coderedcms/blocks/film_strip_block.html

@@ -0,0 +1,32 @@
+{% extends "coderedcms/blocks/base_block.html" %}
+{% load i18n wagtailcore_tags wagtailimages_tags %}
+{% block block_render %}
+{% with value=self.film_strip %}
+<div class="position-relative" data-block="film-strip" data-current-block='0'>
+  <div class="crx-filmstrip-container" data-block="film-container">
+    <div class="row g-0 flex-nowrap" data-block="film-row">
+      {% for panel in value.film_panels.all %}
+        {% image panel.background_image original as image %}
+        <div data-block="film-panel" class="col-auto col crx-filmstrip-panel {{panel.custom_css_class}}" {% if panel.custom_id %}id="{{panel.custom_id}}"{% endif %} style="
+          {% if image %}background-image: url({{image.url}}); background-size: cover; background-position: center;{% endif %}
+          {% if panel.background_color %}background-color: {{panel.background_color}}{% endif %}
+          {% if panel.foreground_color %}color: {{panel.foreground_color}}{% endif %}
+        ">
+          {% include_block panel.content %}
+        </div>
+        {% endfor %}
+    </div>
+  </div>
+  {% block film_strip_buttons %}
+  <div class="row justify-content-between">
+    <button type="button" class="btn col-auto" data-button="left">
+      ← {% trans 'Previous' %}
+    </button>
+    <button type="button" class="btn col-auto" data-button="right">
+      {% trans 'Next' %} →
+    </button>
+  </div>
+  {% endblock film_strip_buttons %}
+</div>
+{% endwith %}
+{% endblock %}

+ 7 - 9
coderedcms/templates/coderedcms/blocks/pagelist_block.html

@@ -1,15 +1,13 @@
 {% extends "coderedcms/blocks/base_block.html" %}
 {% load wagtailcore_tags %}
 {% block block_render %}
-<ul>
+<div class="row">
   {% for page in pages %}
-  {% with page=page.specific %}
-  <li>
-    <a href="{% pageurl page %}">
-      {{page.title}} {% if self.show_preview %}<small class="text-muted">– {{page.body_preview}}</small>{% endif %}
-    </a>
-  </li>
-  {% endwith %}
+  <div class="col-sm-6 col-lg-4">
+    {% with page=page.specific %}
+    {% include page.miniview_template %}
+    {% endwith %}
+  </div>
   {% endfor %}
-</ul>
+</div>
 {% endblock %}

+ 1 - 3
coderedcms/templates/coderedcms/blocks/pagepreview_block.html

@@ -2,8 +2,6 @@
 {% load wagtailcore_tags %}
 {% block block_render %}
 {% with page=self.page.specific %}
-<a href="{% pageurl page %}">
-  {{page.title}} <small class="text-muted">– {{page.body_preview}}</small>
-</a>
+{% include page.miniview_template %}
 {% endwith %}
 {% endblock %}

+ 4 - 0
coderedcms/templates/coderedcms/pages/article_page.html

@@ -37,5 +37,9 @@
     {% endfor %}
   </div>
   {% endblock %}
+
+  {% block related_content %}
+  {{ block.super }}
+  {% endblock %}
 </article>
 {% endblock %}

+ 29 - 8
coderedcms/templates/coderedcms/pages/base.html

@@ -126,22 +126,43 @@
     {% block content_post_body %}{% endblock %}
 
     {% block index_filters %}
-    {% if page.index_show_subpages and page.index_classifiers.exists %}
-    {% include "coderedcms/includes/classifier_dropdowns.html" with formclass="d-flex" formid="filter" %}
-    {% endif %}
+    <div class="container">
+      {% if page.index_show_subpages and page.index_classifiers.exists %}
+      {% include "coderedcms/includes/classifier_dropdowns.html" with formclass="d-flex" formid="filter" %}
+      {% endif %}
+    </div>
     {% endblock %}
 
     {% block index_content %}
     {% if page.index_show_subpages %}
-    <ul>
-      {% for child in index_paginated %}
-      <li><a href="{% pageurl child %}">{{child.title}}</a></li>
-      {% endfor %}
-    </ul>
+    <div class="container">
+      <div class="row">
+        {% for child in index_paginated %}
+        <div class="col-sm-6 col-lg-4">
+          {% include child.miniview_template with page=child %}
+        </div>
+        {% endfor %}
+      </div>
+    </div>
     {% include "coderedcms/includes/pagination.html" with items=index_paginated %}
     {% endif %}
     {% endblock %}
 
+    {% block related_content %}
+    {% if page.related_show %}
+    <div class="container">
+      <h2 class="text-center my-5">{% trans "Related" %}</h2>
+      <div class="row">
+        {% for rp in related_pages %}
+        <div class="col-sm-6 col-lg-4">
+          {% include rp.miniview_template with page=rp %}
+        </div>
+        {% endfor %}
+      </div>
+    </div>
+    {% endif %}
+    {% endblock %}
+
     {% endblock %}
   </div>
 

+ 24 - 0
coderedcms/templates/coderedcms/pages/form_page.mini.html

@@ -0,0 +1,24 @@
+{% load django_bootstrap5 coderedcms_tags wagtailcore_tags %}
+{% with page=self.page.specific %}
+{% if page.form_live %}
+{% get_pageform page request as form %}
+<form class="{{ page.form_css_class }}" id="{{ page.form_id }}" action="{% pageurl page %}" method="POST"
+  {% if form|is_file_form %}enctype="multipart/form-data" {% endif %}>
+  {% csrf_token %}
+  {% bootstrap_form form layout="horizontal" %}
+  {% block captcha %}
+  {% if page.spam_protection %}
+  {% include "coderedcms/includes/form_honeypot.html" %}
+  {% endif %}
+  {% endblock %}
+  <div class="mt-5 row">
+    <div class="{{'horizontal_label_class'|bootstrap_settings}}"></div>
+    <div class="{{'horizontal_field_class'|bootstrap_settings}}">
+      <button type="submit" class="btn {{page.button_size}} {{page.button_style}} {{page.button_css_class}}">
+        {{ page.button_text }}
+      </button>
+    </div>
+  </div>
+</form>
+{% endif %}
+{% endwith %}

+ 12 - 0
coderedcms/templates/coderedcms/pages/page.mini.html

@@ -0,0 +1,12 @@
+{% load wagtailcore_tags wagtailimages_tags %}
+<div class="card mb-3">
+  {% if page.cover_image %}
+  {% image page.cover_image fill-800x450 format-webp as card_img %}
+  <img class="card-img-top w-100" src="{{card_img.url}}" alt="{{card_img.title}}">
+  {% endif %}
+  <div class="card-body">
+    <h5 class="card-title">{{page.title}}</h5>
+    <p class="card-text">{{page.body_preview}}</p>
+    <a class="card-link" href="{% pageurl page %}" title="{{page.title}}">Read more</a>
+  </div>
+</div>

+ 0 - 4
coderedcms/templates/coderedcms/pages/search.html

@@ -63,11 +63,7 @@
   {% for page in results_paginated %}
   <div class="mb-5">
     {% with page=page.specific %}
-    {% if page.search_template %}
     {% include page.search_template %}
-    {% else %}
-    {% include 'coderedcms/pages/search_result.html' %}
-    {% endif %}
     {% endwith %}
   </div>
   {% endfor %}

+ 0 - 16
coderedcms/templates/coderedcms/pages/web_page_notitle.html

@@ -4,22 +4,6 @@
 {% include "coderedcms/snippets/navbar.html" %}
 {% endblock %}
 
-{% block index_filters %}
-{% if page.index_show_subpages and page.index_classifiers.exists %}
-<div class="container">
-  {% include "coderedcms/includes/classifier_dropdowns.html" with formclass="d-flex" formid="filter" %}
-</div>
-{% endif %}
-{% endblock %}
-
-{% block index_content %}
-{% if page.index_show_subpages %}
-<div class="container">
-  {{ block.super }}
-</div>
-{% endif %}
-{% endblock %}
-
 {% block footer %}
 {% include "coderedcms/snippets/footer.html" %}
 {% endblock %}

+ 14 - 0
docs/features/blocks/contentblocks/filmstrip.rst

@@ -0,0 +1,14 @@
+Film Strip Block
+================
+
+Allows the user to choose a long horizontal row of scrollable panels.
+
+
+Field Reference
+---------------
+
+Fields and purposes:
+
+* **Film Strip** - Choose a :doc:`/features/snippets/filmstrip` snippet.
+
+If you don't have any film strips already made, you can build a carousel by clicking **Choose a Film Strip** and clicking on "Why not **create one now**?" in the popup box. This will take you to **Snippets > Film Strips** where you can create a carousel to add to the page.

+ 2 - 2
docs/features/blocks/contentblocks/index.rst

@@ -14,6 +14,7 @@ Content blocks are the blocks for adding various types of content to your site.
     carousel
     download
     embedmedia
+    filmstrip
     googlemap
     html
     image
@@ -23,8 +24,7 @@ Content blocks are the blocks for adding various types of content to your site.
     modal
     pagepreview
     pricelist
-    reusablecontent
     quote
+    reusablecontent
     table
     text
-

+ 19 - 13
docs/features/blocks/contentblocks/latestpages.rst

@@ -1,7 +1,7 @@
 Latest Pages Block
 ==================
 
-Creates a list of the most recently published pages with a specified length. 
+Creates a list of the most recently published pages with a specified length.
 
 Field Reference
 ---------------
@@ -10,23 +10,29 @@ Fields and purposes:
 
 * **Parent Page** - Shows a preview of pages that are children of the selected page. Uses ordering specified in the page’s LAYOUT tab.
 
-* **Classified By** - Filters which pages are displayed by the classifier that you selected
+* **Classified By** - Filters which pages are displayed by the classifier that you selected.
 
-* **Show Body Preview** - If selected, shows a preview of what the page contains 
+* **Number of Pages to Show** - Limits how many pages are displayed to the number that you selected.
 
-* **Number of Pages to Show** - Limits how many pages are displayed to the number that you selected
-
-.. note::
-    There are also a few built-in templates available for this block under the **Advanced Settings** section.
-
-The pages are displayed as links with a line or so of text if the preview option is selected.
+Each page is rendered using the page model's "miniview" template.
+The template can be overridden per model with the ``miniview_template`` attribute, the default of which is `coderedcms/pages/page.mini.html <https://github.com/coderedcorp/coderedcms/blob/dev/coderedcms/templates/coderedcms/pages/pages.mini.html>`_.
 
 .. figure:: img/latestpages1.png
-    :alt: The Latest Pages block and its settings
+    :alt: The Latest Pages block and its settings.
 
-    The Latest Pages block and its settings
+    The Latest Pages block and its settings.
 
 .. figure:: img/latestpages2.png
-    :alt: The Latest Pages block as displayed on the website
+    :alt: The Latest Pages block as displayed on the website.
+
+    The Latest Pages block as displayed on the website.
+
+.. versionadded:: 2.1
+
+   Miniview templates were added in Wagtail CRX 2.1
+
+.. deprecated:: 2.1
+
+   * "Show Body Preview" field was deprecated in 2.1 and will be removed in 3.0.
 
-    The Latest Pages block as displayed on the website
+   * The additional built-in templates under this block's **Advanced Settings** are deprecated as of 2.1 and will be removed in 3.0. These have been replaced with identical miniview templates for Article and Form pages.

+ 9 - 5
docs/features/blocks/contentblocks/pagepreview.rst

@@ -1,7 +1,7 @@
 Page Preview Block
 ==================
 
-Shows a preview of a selected Page
+Shows a miniview (a condensed version) of a selected Page.
 
 Field Reference
 ---------------
@@ -11,9 +11,13 @@ There is only one field.
 **Page to Preview** - Select the page that you want to display a preview
 
 .. figure:: img/pagepreview_edit.png
-    :alt: A Page Preview block and its settings
+    :alt: A Page Preview block and its settings.
 
-    A Page Preview block and its settings
+    A Page Preview block and its settings.
 
-It looks very similar in design to the Latest Pages block but only displays the one selected page.
-It shows a link to the page and a few lines of preview text.
+The selected page is rendered using the page model's "miniview" template.
+The template can be overridden per model with the ``miniview_template`` attribute, the default of which is `coderedcms/pages/page.mini.html <https://github.com/coderedcorp/coderedcms/blob/dev/coderedcms/templates/coderedcms/pages/pages.mini.html>`_.
+
+.. versionadded:: 2.1
+
+   Miniview templates were added in Wagtail CRX 2.1

二進制
docs/features/img/related_pages.png


+ 1 - 0
docs/features/index.rst

@@ -8,5 +8,6 @@ Features
     import_export
     mailchimp
     page_types/index
+    related_pages
     searching
     snippets/index

+ 63 - 0
docs/features/related_pages.rst

@@ -0,0 +1,63 @@
+Related Pages
+=============
+
+Using the power of :doc:`/features/snippets/classifiers`, pages can automatically show a list of similarly classified pages. By default, this is enabled on :doc:`/features/page_types/article_pages`, but can be enabled on any page via the Wagtail Admin, or a 1-line code change on the page model.
+
+.. figure:: img/related_pages.png
+   :alt:  Related pages showing similarly classified articles.
+
+   Related pages showing similarly classified articles.
+
+
+Related page formatting
+------------------------
+
+Each related page is rendered using the page model's "miniview" template.
+The template can be overridden per model with the ``miniview_template`` attribute, the default of which is `coderedcms/pages/page.mini.html <https://github.com/coderedcorp/coderedcms/blob/dev/coderedcms/templates/coderedcms/pages/pages.mini.html>`_.
+
+If related pages are enabled, a ``QuerySet`` of pages is added to the context as ``related_pages``. This ``QuerySet`` can also be retrieved by calling ``page.get_related_pages()``.
+
+
+Related page customization
+--------------------------
+
+Each page can have related pages turned on or off via a toggle in the page editor, under the **Layout** tab. Pages based on ``CoderedArticlePage`` have this setting enabled by default when new pages are created. To toggle the default value when creating new pages, set ``related_show_default`` to ``True`` or ``False``. To retroactively toggle this setting on existing pages, set the related ``related_show`` field using a manual query or migration.
+
+By default, sibling pages are queried and ordered based on the number of Classifier Terms in common. If you wish to query a different model --- for example to have Article pages show related Product pages instead --- set the ``related_query_pagemodel`` attribute to the desired content type.
+
+.. code-block:: python
+
+   class ProductPage(CoderedPage):
+
+       # Custom template that will be used when a Product
+       # is shown on an Article page (below).
+       miniview_template = "website/pages/product.mini.html"
+
+   class ArticlePage(CoderedPage):
+
+       # Show related pages by default when creating new Articles.
+       related_show_default = True
+
+       # By default, related sibling Articles will be shown.
+       # Show related Products instead.
+       related_query_pagemodel = "website.ProductPage"
+
+If you'd instead prefer to totally customize the related algorithm, override the ``get_related_pages()`` function. Just be sure to return a ``QuerySet`` of pages.
+
+.. code-block:: python
+
+   class ProductPage(CoderedPage):
+       price = models.DecimalField(max_digits=9, decimal_places=2)
+
+   class ArticlePage(CoderedPage):
+
+       # Show related pages by default when creating new Articles.
+       related_show_default = True
+
+       def get_related_pages(self) -> models.QuerySet:
+           """Show most expensive products first."""
+           return ProductPage.objects.live().order_by("-price")
+
+.. versionadded:: 2.1
+
+   You must be on Wagtail CRX version 2.1 or higher to use related pages.

+ 38 - 0
docs/features/snippets/filmstrip.rst

@@ -0,0 +1,38 @@
+Film Strip
+==========
+
+A long horizontal row of scrollable panels, similar to a reel of film.
+
+.. figure:: img/filmstrip_block.png
+   :alt:  The film strip block, with four panels (three fully visible).
+
+   The film strip block, with four panels (three fully visible).
+
+
+Usage
+-----
+
+You define your film strips and their panels in the **Snippets > Film Strips** section of the admin.  Once defined, any page with a body streamfield can show that film strip by selecting it with a :doc:`/features/blocks/contentblocks/filmstrip`.
+
+Fields
+------
+
+Slider
+~~~~~~
+**Name**: A unique name for your film strip.  It can be anything, it is just used as a personal reference to easily tell them apart within Wagtail.
+
+Panels
+~~~~~~
+
+To add a slide to your carousel, click the "Add Slides" button.
+
+**Background image**: An optional image that will fill the entire panel. May be cropped or scaled to fit.
+**Background color**: An optional color that will fill the entire panel.
+**Foreground color**: An optional color used for text in the panel.
+**Custom CSS class**: If you need to add a specific CSS class for this panel, add it here.
+**Custom ID**: If you need to add a specific ID for this panel, add it here.
+**Content**: A streamfield that contains the content blocks for the panel.
+
+.. versionadded:: 2.1
+
+   You must be on Wagtail CRX version 2.1 or higher to use Film Strips.

二進制
docs/features/snippets/img/filmstrip_block.png


+ 1 - 0
docs/features/snippets/index.rst

@@ -11,6 +11,7 @@ functionality.
     carousels
     classifiers
     content_walls
+    filmstrip
     footers
     navigation_bars
     reusable_content

+ 1 - 1
docs/how_to/docker.rst

@@ -66,7 +66,7 @@ extension). Copy the contents below into the file:
 
     # Finally, run the app on port 8000.
     EXPOSE 8000
-    CMD exec waitress serve --listen "*:8000" "myproject.wsgi:application"
+    CMD exec waitress-serve --listen "*:8000" "myproject.wsgi:application"
 
 
 The "Image as Environment" Approach

+ 15 - 4
docs/releases/index.rst

@@ -6,15 +6,26 @@ Wagtail CRX (previously CodeRed CMS) follows the
 
 * **Major** - significant changes that may not be backwards compatible.
 
-* **Minor** - new features, enhancements, and bug fixes that are most likely
-  but not guaranteed to be backwards compatible.
+* **Minor** - new features, enhancements, and bug fixes that are most likely but not guaranteed to be backwards compatible.
 
-* **Patch** - guaranteed to be backwards compatible. These changes are
-  reserved for bug, security, or documentation fixes only.
+* **Patch** - guaranteed to be backwards compatible. These changes are reserved for bug, security, or documentation fixes only.
+
+Supported Versions:
+
++--------------+---------+----------+---------------------------+
+| CRX version  | Wagtail | Python   | Support Status            |
++==============+=========+==========+===========================+
+| CRX 2.x      | 4.x     | 3.7-3.11 | Supported                 |
++--------------+---------+----------+---------------------------+
+| CRX 1.x      | 3.x     | 3.7-3.11 | Support contract required |
++--------------+---------+----------+---------------------------+
+| CRX 0.25     | 2.16    | 3.7-3.10 | Support contract required |
++--------------+---------+----------+---------------------------+
 
 .. toctree::
     :maxdepth: 1
 
+    v2.1.0
     v2.0.0
     v1.0.3
     v1.0.2

+ 2 - 2
docs/releases/v2.0.0.rst

@@ -5,7 +5,7 @@ v2.0.0 release notes
 New features
 ------------
 
-* CRX no longer pins the minor version of Wagtail. 2.x series will work with any version of Wagtail 4.
+* CRX no longer pins the minor version of Wagtail.
 
 * Support Wagtail 4.0, 4.1, and 4.2.
 
@@ -13,7 +13,7 @@ New features
 Upgrade considerations
 ----------------------
 
-While there are no functional changes in CRX, there are many changes in Wagtail 4. Most CRX sites should not be impacted, however if your site implements custom functionality beyond what CRX provides, review the `Wagtail 4 release notes and upgrade considerations <https://docs.wagtail.org/en/stable/releases/4.0.html>`.
+While there are no functional changes in CRX, there are many changes in Wagtail 4. Most CRX sites should not be impacted, however if your site implements custom functionality beyond what CRX provides, review the `Wagtail 4 release notes and upgrade considerations <https://docs.wagtail.org/en/stable/releases/4.0.html>`_.
 
 If you are upgrading from an older version of CRX (0.x series) it is highly recommended to upgrade to 1.0 before attempting to upgrade to 2.0.
 

+ 53 - 0
docs/releases/v2.1.0.rst

@@ -0,0 +1,53 @@
+v2.1.0 release notes
+====================
+
+
+New features
+------------
+
+* New snippet and block: :doc:`/features/snippets/filmstrip`.
+
+* New system-wide feature: :doc:`/features/related_pages`.
+
+* New template rendering option for page models, the "miniview". Instead of a full page template, this is used to render a small preview of the page in other places on the site, usually in a Bootstrap card format. The miniview template is used by:
+
+  * :doc:`/features/related_pages`
+
+  * :doc:`/features/blocks/contentblocks/pagepreview`
+
+  * :doc:`/features/blocks/contentblocks/latestpages`
+
+
+Upgrade considerations
+----------------------
+
+The built-in template options under Page Preview and Latest Pages block **Advanced Settings** are deprecated as of 2.1 and will be removed in 3.0. These have been replaced with identical miniview templates for Article and Form pages. **If you have overridden these templates** it is recommended to set your custom instead on the page model's ``miniview_template`` field.
+
+The new related pages feature is on by default for any newly created pages based on ``CoderedArticlePage``.
+
+* If you do not want this feature, disable it by setting ``related_show_default=False`` on your Article model.
+
+* If you want this feature retroactively on existing pages, write a simple query or migration to turn it on for those pages:
+
+  .. code-block:: python
+
+     # To enable related pages on just Article pages:
+     from website.models import ArticlePage
+     ArticlePage.objects.all().update(related_show=True)
+
+     # To enable related pages on all pages of the site:
+     from coderedcms.models import CoderedPage
+     CoderedPage.objects.all().update(related_show=True)
+
+As always, be sure to make and apply migrations after upgrading:
+
+.. code-block:: text
+
+   python manage.py makemigrations
+   python manage.py migrate
+
+
+Thank you!
+----------
+
+Thanks to everyone who contributed to `2.1.0 on GitHub <https://github.com/coderedcorp/coderedcms/milestone/46?closed=1>`_.

部分文件因文件數量過多而無法顯示