Add block types report (#11041)
* Add block types report * Annotate content_types to query * Render page types blocks are used on * Render block name and status * Show remaining page types in tooltip * Update gitignore * Custom css for report * Remove not needed !important * Lint * Fix malformed HTML * Rename tag block * Add tests to tags * Add more test cases * Lint * Lint * Lint * Annotate content_types to query * Render page types blocks are used on * Show remaining page types in tooltip * Custom css for report * Lint * Rename tag block * Improve tags code readability * Better code blocks nomenclature * Use defaultdict * Only count live pages
This commit is contained in:
Родитель
9aa4d3bcd2
Коммит
1b9a749ff0
|
@ -92,6 +92,7 @@ celerybeat-schedule
|
|||
|
||||
# virtualenv
|
||||
venv/
|
||||
.venv/
|
||||
ENV/
|
||||
dockerpythonvenv/
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
.w-block-report-title {
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
text-align: start;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.w-block-report-tippy {
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
text-align: start;
|
||||
line-height: inherit;
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 1px;
|
||||
}
|
||||
|
||||
.tippy-content a {
|
||||
color: var(--w-color-white);
|
||||
text-decoration: dotted;
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 1px;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
from django import template
|
||||
from django.urls import reverse
|
||||
from django.utils.html import format_html, mark_safe
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_content_type(content_type):
|
||||
"""Render a Content Type with a link to the content type's admin page"""
|
||||
return format_html(
|
||||
"<a href='{}'>{}</a>",
|
||||
reverse(
|
||||
"wagtailadmin_pages:type_use",
|
||||
kwargs={
|
||||
"content_type_app_name": content_type.app_label,
|
||||
"content_type_model_name": content_type.model,
|
||||
},
|
||||
),
|
||||
content_type.name.title(),
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_content_types(content_types):
|
||||
"""Render a list of content types"""
|
||||
return mark_safe(", ".join([render_content_type(content_type) for content_type in content_types]))
|
||||
|
||||
|
||||
@register.inclusion_tag("tags/reports/page_types_block.html")
|
||||
def page_types_block(content_types):
|
||||
content_types_hidden = []
|
||||
count_hidden = 0
|
||||
if len(content_types) > 3:
|
||||
content_types_hidden = content_types[3:]
|
||||
content_types = content_types[:3]
|
||||
count_hidden = len(content_types_hidden)
|
||||
return {
|
||||
"content_types_shown": content_types,
|
||||
"content_types_hidden": content_types_hidden,
|
||||
"count_hidden": count_hidden,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag("tags/reports/block_name.html")
|
||||
def block_name(page_block):
|
||||
full_name = page_block["block"]
|
||||
short_name = full_name.split(".")[-1]
|
||||
return {
|
||||
"full_name": full_name,
|
||||
"short_name": short_name,
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.urls import reverse
|
||||
from factory import Faker
|
||||
from wagtailinventory.helpers import create_page_inventory, delete_page_inventory
|
||||
|
||||
from networkapi.reports.views import BlockTypesReportView
|
||||
from networkapi.utility.faker import StreamfieldProvider
|
||||
from networkapi.wagtailpages.factory.campaign_page import CampaignPageFactory
|
||||
from networkapi.wagtailpages.factory.opportunity import OpportunityPageFactory
|
||||
from networkapi.wagtailpages.factory.primary_page import PrimaryPageFactory
|
||||
from networkapi.wagtailpages.tests.base import WagtailpagesTestCase
|
||||
|
||||
Faker.add_provider(StreamfieldProvider)
|
||||
|
||||
|
||||
class PatchedPrimaryPageFactory(PrimaryPageFactory):
|
||||
body = Faker("streamfield", fields=["paragraph", "image", "airtable"])
|
||||
|
||||
|
||||
class PatchedCampaignPageFactory(CampaignPageFactory):
|
||||
body = Faker("streamfield", fields=["paragraph", "image"])
|
||||
|
||||
|
||||
class PatchedOpportunityPageFactory(OpportunityPageFactory):
|
||||
body = Faker("streamfield", fields=["paragraph"])
|
||||
|
||||
|
||||
class BlockTypesReportViewTest(WagtailpagesTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.view = BlockTypesReportView()
|
||||
User = get_user_model()
|
||||
self.user = User.objects.create_superuser("admin-user", "admin@example.com", "password")
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_view(self):
|
||||
"""Tests that the queryset is correct."""
|
||||
# Create some pages with custom and standard blocks
|
||||
primary_page = PatchedPrimaryPageFactory(parent=self.homepage)
|
||||
campaign_page = PatchedCampaignPageFactory(parent=self.homepage)
|
||||
opportunity_page = PatchedOpportunityPageFactory(parent=self.homepage)
|
||||
|
||||
# Update `wagtailinventory`'s index
|
||||
create_page_inventory(primary_page)
|
||||
create_page_inventory(campaign_page)
|
||||
create_page_inventory(opportunity_page)
|
||||
|
||||
# Request the view
|
||||
response = self.client.get(reverse("block_types_report"))
|
||||
|
||||
# Get the objects:
|
||||
object_list = response.context["object_list"]
|
||||
|
||||
# The first, most used, should be the RichTextBlock created on all three pages
|
||||
block = object_list[0]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.RichTextBlock")
|
||||
self.assertEqual(block["count"], 3)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual(
|
||||
[primary_page.content_type, campaign_page.content_type, opportunity_page.content_type],
|
||||
block["content_types"],
|
||||
)
|
||||
|
||||
# Two pages have crated ImageBlocks
|
||||
# Each ImageBlock has a ImageChooserBlock and a CharBlock
|
||||
# These should be in alphabetical order since they all have the same count
|
||||
block = object_list[1]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.annotated_image_block.AnnotatedImageBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 2)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type, campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[2]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.annotated_image_block.RadioSelectBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 2)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type, campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[3]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.CharBlock")
|
||||
self.assertEqual(block["count"], 2)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type, campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[4]
|
||||
self.assertEqual(block["block"], "wagtail.images.blocks.ImageChooserBlock")
|
||||
self.assertEqual(block["count"], 2)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type, campaign_page.content_type], block["content_types"])
|
||||
|
||||
# FInally, we have the AirTableBlock, use only in one page
|
||||
# This one is made of a URLBlock and a IntegerBlock
|
||||
block = object_list[5]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.airtable_block.AirTableBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[6]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.IntegerBlock")
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[7]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.URLBlock")
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([primary_page.content_type], block["content_types"])
|
||||
|
||||
def test_page_unpublished(self):
|
||||
"""Tests that the queryset is updated when a page is unpublished"""
|
||||
# Create some pages with custom and standard blocks
|
||||
primary_page = PatchedPrimaryPageFactory(parent=self.homepage)
|
||||
campaign_page = PatchedCampaignPageFactory(parent=self.homepage)
|
||||
opportunity_page = PatchedOpportunityPageFactory(parent=self.homepage)
|
||||
|
||||
# Update `wagtailinventory`'s index
|
||||
create_page_inventory(primary_page)
|
||||
create_page_inventory(campaign_page)
|
||||
create_page_inventory(opportunity_page)
|
||||
|
||||
# Unpublish primary page
|
||||
primary_page.unpublish()
|
||||
|
||||
# Request the view
|
||||
response = self.client.get(reverse("block_types_report"))
|
||||
|
||||
# Get the objects:
|
||||
object_list = response.context["object_list"]
|
||||
|
||||
# The first, most used, should be the RichTextBlock created on the two live pages
|
||||
block = object_list[0]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.RichTextBlock")
|
||||
self.assertEqual(block["count"], 2)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual(
|
||||
[campaign_page.content_type, opportunity_page.content_type],
|
||||
block["content_types"],
|
||||
)
|
||||
|
||||
# Two pages have crated ImageBlocks
|
||||
# Each ImageBlock has a ImageChooserBlock and a CharBlock
|
||||
# These should be in alphabetical order since they all have the same count
|
||||
block = object_list[1]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.annotated_image_block.AnnotatedImageBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[2]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.annotated_image_block.RadioSelectBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[3]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.CharBlock")
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[4]
|
||||
self.assertEqual(block["block"], "wagtail.images.blocks.ImageChooserBlock")
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
def test_page_deleted(self):
|
||||
"""Tests that the queryset is updated when a page is deleted"""
|
||||
# Create some pages with custom and standard blocks
|
||||
primary_page = PatchedPrimaryPageFactory(parent=self.homepage)
|
||||
campaign_page = PatchedCampaignPageFactory(parent=self.homepage)
|
||||
opportunity_page = PatchedOpportunityPageFactory(parent=self.homepage)
|
||||
|
||||
# Update `wagtailinventory`'s index
|
||||
create_page_inventory(primary_page)
|
||||
create_page_inventory(campaign_page)
|
||||
create_page_inventory(opportunity_page)
|
||||
|
||||
# Delete primary page
|
||||
primary_page.delete()
|
||||
|
||||
# Update the inventory
|
||||
delete_page_inventory(primary_page)
|
||||
|
||||
# Request the view
|
||||
response = self.client.get(reverse("block_types_report"))
|
||||
|
||||
# Get the objects:
|
||||
object_list = response.context["object_list"]
|
||||
|
||||
# The first, most used, should be the RichTextBlock created on the two live pages
|
||||
block = object_list[0]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.RichTextBlock")
|
||||
self.assertEqual(block["count"], 2)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual(
|
||||
[campaign_page.content_type, opportunity_page.content_type],
|
||||
block["content_types"],
|
||||
)
|
||||
|
||||
# Two pages have crated ImageBlocks
|
||||
# Each ImageBlock has a ImageChooserBlock and a CharBlock
|
||||
# These should be in alphabetical order since they all have the same count
|
||||
block = object_list[1]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.annotated_image_block.AnnotatedImageBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[2]
|
||||
self.assertEqual(
|
||||
block["block"], "networkapi.wagtailpages.pagemodels.customblocks.annotated_image_block.RadioSelectBlock"
|
||||
)
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Custom")
|
||||
self.assertTrue(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[3]
|
||||
self.assertEqual(block["block"], "wagtail.blocks.field_block.CharBlock")
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
||||
|
||||
block = object_list[4]
|
||||
self.assertEqual(block["block"], "wagtail.images.blocks.ImageChooserBlock")
|
||||
self.assertEqual(block["count"], 1)
|
||||
self.assertEqual(block["type_label"], "Core")
|
||||
self.assertFalse(block["is_custom_block"])
|
||||
self.assertListEqual([campaign_page.content_type], block["content_types"])
|
|
@ -0,0 +1,108 @@
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import SimpleTestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.html import format_html, mark_safe
|
||||
|
||||
from networkapi.reports.templatetags import report_tags
|
||||
|
||||
|
||||
class RenderContentTypeTagTests(SimpleTestCase):
|
||||
def test_render_content_type(self):
|
||||
content_type = ContentType(app_label="myapp", model="mymodel")
|
||||
expected_link = reverse(
|
||||
"wagtailadmin_pages:type_use",
|
||||
kwargs={
|
||||
"content_type_app_name": content_type.app_label,
|
||||
"content_type_model_name": content_type.model,
|
||||
},
|
||||
)
|
||||
expected_name = "Mymodel"
|
||||
|
||||
result = report_tags.render_content_type(content_type)
|
||||
|
||||
self.assertHTMLEqual(result, format_html("<a href='{}'>{}</a>", expected_link, expected_name))
|
||||
self.assertIsInstance(result, str)
|
||||
self.assertTrue(mark_safe(result))
|
||||
|
||||
|
||||
class RenderContentTypesTagTests(SimpleTestCase):
|
||||
def test_render_list_of_content_types(self):
|
||||
content_types = [
|
||||
ContentType(app_label="myapp", model="mymodel1"),
|
||||
ContentType(app_label="myapp", model="mymodel2"),
|
||||
ContentType(app_label="myapp", model="mymodel3"),
|
||||
]
|
||||
expected_link_1 = reverse(
|
||||
"wagtailadmin_pages:type_use",
|
||||
kwargs={
|
||||
"content_type_app_name": content_types[0].app_label,
|
||||
"content_type_model_name": content_types[0].model,
|
||||
},
|
||||
)
|
||||
expected_link_2 = reverse(
|
||||
"wagtailadmin_pages:type_use",
|
||||
kwargs={
|
||||
"content_type_app_name": content_types[1].app_label,
|
||||
"content_type_model_name": content_types[1].model,
|
||||
},
|
||||
)
|
||||
expected_link_3 = reverse(
|
||||
"wagtailadmin_pages:type_use",
|
||||
kwargs={
|
||||
"content_type_app_name": content_types[2].app_label,
|
||||
"content_type_model_name": content_types[2].model,
|
||||
},
|
||||
)
|
||||
|
||||
expected_html = """
|
||||
<a href='{}'>{}</a>, <a href='{}'>{}</a>, <a href='{}'>{}</a>
|
||||
""".format(
|
||||
expected_link_1,
|
||||
"Mymodel1",
|
||||
expected_link_2,
|
||||
"Mymodel2",
|
||||
expected_link_3,
|
||||
"Mymodel3",
|
||||
)
|
||||
|
||||
result = report_tags.render_content_types(content_types)
|
||||
|
||||
self.assertHTMLEqual(result, expected_html)
|
||||
self.assertIsInstance(result, str)
|
||||
self.assertTrue(mark_safe(result))
|
||||
|
||||
|
||||
class PageTypesBlockTagTests(SimpleTestCase):
|
||||
def test_page_types_block(self):
|
||||
content_types = [
|
||||
ContentType(app_label="myapp", model="mymodel1"),
|
||||
ContentType(app_label="myapp", model="mymodel2"),
|
||||
ContentType(app_label="myapp", model="mymodel3"),
|
||||
ContentType(app_label="myapp", model="mymodel4"),
|
||||
]
|
||||
|
||||
result = report_tags.page_types_block(content_types)
|
||||
|
||||
self.assertIsInstance(result, dict)
|
||||
self.assertIn("content_types_shown", result)
|
||||
self.assertIn("content_types_hidden", result)
|
||||
self.assertIn("count_hidden", result)
|
||||
self.assertEqual(result["content_types_shown"], content_types[:3])
|
||||
self.assertEqual(result["content_types_hidden"], content_types[3:])
|
||||
self.assertEqual(result["count_hidden"], 1)
|
||||
|
||||
|
||||
class BlockNameTagTests(SimpleTestCase):
|
||||
def test_block_name(self):
|
||||
page_block = {
|
||||
"block": "myapp.mymodel.MyBlock",
|
||||
"count": 1,
|
||||
}
|
||||
|
||||
result = report_tags.block_name(page_block)
|
||||
|
||||
self.assertIsInstance(result, dict)
|
||||
self.assertIn("full_name", result)
|
||||
self.assertIn("short_name", result)
|
||||
self.assertEqual(result["full_name"], "myapp.mymodel.MyBlock")
|
||||
self.assertEqual(result["short_name"], "MyBlock")
|
|
@ -1,11 +1,14 @@
|
|||
from collections import defaultdict
|
||||
|
||||
import django_filters
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db.models import Count, OuterRef, Q, Subquery
|
||||
from django.db.models import BooleanField, Case, Count, OuterRef, Q, Subquery, When
|
||||
from wagtail.admin.filters import WagtailFilterSet
|
||||
from wagtail.admin.views.reports import ReportView
|
||||
from wagtail.coreutils import get_content_languages
|
||||
from wagtail.models import ContentType, Page, PageLogEntry, get_page_models
|
||||
from wagtail.users.utils import get_deleted_user_display_name
|
||||
from wagtailinventory.models import PageBlock
|
||||
|
||||
|
||||
def _get_locale_choices():
|
||||
|
@ -46,7 +49,7 @@ class PageTypesReportFilterSet(WagtailFilterSet):
|
|||
|
||||
|
||||
class PageTypesReportView(ReportView):
|
||||
title = "Page Types Report"
|
||||
title = "Page types report"
|
||||
template_name = "pages/reports/page_types_report.html"
|
||||
header_icon = "doc-empty-inverse"
|
||||
|
||||
|
@ -93,3 +96,44 @@ class PageTypesReportView(ReportView):
|
|||
queryset = queryset.order_by("-count", "app_label", "model")
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
class BlockTypesReportView(ReportView):
|
||||
title = "Block types report"
|
||||
template_name = "pages/reports/block_types_report.html"
|
||||
header_icon = "placeholder"
|
||||
|
||||
def decorate_paginated_queryset(self, object_list):
|
||||
# Build a cache map of PageBlock's block name to content types
|
||||
page_blocks = PageBlock.objects.all().prefetch_related("page__content_type")
|
||||
blocks_to_content_types = defaultdict(list)
|
||||
for page_block in page_blocks:
|
||||
if page_block.page.live and (
|
||||
page_block.page.content_type not in blocks_to_content_types[page_block.block]
|
||||
):
|
||||
blocks_to_content_types[page_block.block].append(page_block.page.content_type)
|
||||
|
||||
# Get the content_types for each block name
|
||||
for block_report_item in object_list:
|
||||
content_types = blocks_to_content_types.get(block_report_item["block"], [])
|
||||
block_report_item["content_types"] = content_types
|
||||
block_report_item["type_label"] = "Custom" if block_report_item["is_custom_block"] else "Core"
|
||||
|
||||
return object_list
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = (
|
||||
PageBlock.objects.all()
|
||||
.values("block")
|
||||
.annotate(
|
||||
count=Count("page", filter=Q(page__live=True)),
|
||||
is_custom_block=Case(
|
||||
When(block__startswith="wagtail.", then=False), default=True, output_field=BooleanField()
|
||||
),
|
||||
)
|
||||
)
|
||||
self.queryset = queryset
|
||||
|
||||
queryset = queryset.order_by("-count", "block")
|
||||
|
||||
return queryset
|
||||
|
|
|
@ -2,13 +2,13 @@ from django.urls import path, reverse
|
|||
from wagtail import hooks
|
||||
from wagtail.admin.menu import AdminOnlyMenuItem
|
||||
|
||||
from .views import PageTypesReportView
|
||||
from .views import BlockTypesReportView, PageTypesReportView
|
||||
|
||||
|
||||
@hooks.register("register_reports_menu_item")
|
||||
def register_page_types_report_menu_item():
|
||||
return AdminOnlyMenuItem(
|
||||
"Page Types", reverse("page_types_report"), icon_name=PageTypesReportView.header_icon, order=700
|
||||
"Page types", reverse("page_types_report"), icon_name=PageTypesReportView.header_icon, order=700
|
||||
)
|
||||
|
||||
|
||||
|
@ -17,3 +17,17 @@ def register_page_types_report_url():
|
|||
return [
|
||||
path("reports/page-types-report/", PageTypesReportView.as_view(), name="page_types_report"),
|
||||
]
|
||||
|
||||
|
||||
@hooks.register("register_reports_menu_item")
|
||||
def register_block_types_report_menu_item():
|
||||
return AdminOnlyMenuItem(
|
||||
"Block types", reverse("block_types_report"), icon_name=BlockTypesReportView.header_icon, order=701
|
||||
)
|
||||
|
||||
|
||||
@hooks.register("register_admin_urls")
|
||||
def register_block_types_report_url():
|
||||
return [
|
||||
path("reports/block-types-report/", BlockTypesReportView.as_view(), name="block_types_report"),
|
||||
]
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "wagtailadmin/reports/base_report.html" %}
|
||||
{% load wagtailadmin_tags static %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link rel="stylesheet" href="{% static 'css/reports.css' %}">
|
||||
{% endblock extra_css %}
|
||||
|
||||
{% block results %}
|
||||
{% with object_list as block_report_items %}
|
||||
<div id="page-results">
|
||||
{% if block_report_items %}
|
||||
{% block listing %}
|
||||
{% include "pages/reports/include/_list_block_types.html" %}
|
||||
{% endblock listing %}
|
||||
{% else %}
|
||||
{% block no_results %}
|
||||
<p>No block types match this report's criteria.</p>
|
||||
{% endblock no_results %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endblock results %}
|
|
@ -0,0 +1,54 @@
|
|||
{% load wagtailadmin_tags wagtailcore_tags report_tags %}
|
||||
|
||||
<table class="listing {% block table_classname %}{% endblock table_classname %}">
|
||||
<col width="25%" />
|
||||
<col width="10%" />
|
||||
<col width="10%" />
|
||||
<col />
|
||||
<thead>
|
||||
{% block post_parent_page_headers %}
|
||||
<tr class="table-headers">
|
||||
<th class="title">
|
||||
Block
|
||||
</th>
|
||||
<th class="app-label">
|
||||
Pages
|
||||
</th>
|
||||
<th class="app-label">
|
||||
Type
|
||||
</th>
|
||||
<th class="app-label">
|
||||
Used on
|
||||
</th>
|
||||
{% block extra_columns %}
|
||||
{% endblock extra_columns %}
|
||||
</tr>
|
||||
{% endblock post_parent_page_headers %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if block_report_items %}
|
||||
{% for block_report_item in block_report_items %}
|
||||
<tr class="{% block page_row_classname %}{% endblock page_row_classname %}">
|
||||
<td class="app-label" valign="top">
|
||||
{% block_name block_report_item %}
|
||||
</td>
|
||||
<td class="app-label" valign="top">
|
||||
{{ block_report_item.count }}
|
||||
</td>
|
||||
<td class="app-label" valign="top">
|
||||
<span class="status-tag status-tag--label">{{ block_report_item.type_label }}</span>
|
||||
</td>
|
||||
<td class="app-label" valign="top">
|
||||
{% page_types_block block_report_item.content_types %}
|
||||
</td>
|
||||
{% block extra_page_data %}
|
||||
{% endblock extra_page_data %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% block no_results %}
|
||||
<p>No blocks found.</p>
|
||||
{% endblock no_results %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,5 @@
|
|||
{% load wagtailcore_tags wagtailadmin_tags i18n %}
|
||||
|
||||
<button type="button" class="w-block-report-title" data-tippy-content="{{ full_name }}" data-tippy-maxWidth="50rem">
|
||||
{{ short_name }}
|
||||
</button>
|
|
@ -0,0 +1,15 @@
|
|||
{% load report_tags %}
|
||||
|
||||
{% render_content_types content_types_shown %}
|
||||
{% if content_types_hidden %}
|
||||
and
|
||||
<button type="button"
|
||||
class="w-block-report-tippy"
|
||||
data-tippy-content="{% render_content_types content_types_hidden %}"
|
||||
data-tippy-allowHTML="true"
|
||||
data-tippy-interactive="true"
|
||||
data-tippy-trigger="click"
|
||||
>
|
||||
{{ count_hidden }} more
|
||||
</button>
|
||||
{% endif %}
|
Загрузка…
Ссылка в новой задаче