Skip to content

Commit 7a6cb45

Browse files
committed
Adapt component display to show all info in one row
1 parent f828a14 commit 7a6cb45

File tree

13 files changed

+279
-75
lines changed

13 files changed

+279
-75
lines changed

bundle/Controller/Admin/Components/Index.php

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Netgen\Bundle\LayoutsIbexaBundle\Controller\Admin\Controller;
1515
use Netgen\Layouts\Ibexa\AdminUI\ComponentLayoutsLoader;
1616
use Netgen\Layouts\Ibexa\Form\ComponentFilterType;
17+
use Netgen\Layouts\Ibexa\Search\Contracts\Criterion\IsComponentUsed;
1718
use Pagerfanta\Adapter\AdapterInterface;
1819
use Pagerfanta\Pagerfanta;
1920
use Pagerfanta\PagerfantaInterface;
@@ -23,7 +24,6 @@
2324

2425
use function array_keys;
2526
use function array_values;
26-
use function ksort;
2727
use function max;
2828

2929
final class Index extends Controller
@@ -54,10 +54,9 @@ public function __invoke(Request $request): Response
5454
return $this->render(
5555
'@NetgenLayoutsIbexa/admin/components/index.html.twig',
5656
[
57-
'components' => $this->extractComponents($pager),
57+
'components' => $pager,
5858
'component_layouts' => $this->componentLayoutsLoader->loadLayoutsData(),
5959
'filter_form' => $filterForm->createView(),
60-
'pager' => $pager,
6160
],
6261
);
6362
}
@@ -85,40 +84,32 @@ private function getComponentsQuery(FormInterface $form): LocationQuery
8584
'netgen_layouts',
8685
);
8786

88-
$locationQuery = new LocationQuery();
89-
$locationQuery->filter = new Criterion\LogicalAnd(
90-
[
91-
new Criterion\ParentLocationId(array_values($parentLocationsConfig)),
92-
new Criterion\Location\IsMainLocation(Criterion\Location\IsMainLocation::MAIN),
93-
new Criterion\ContentTypeIdentifier(
94-
$form->get('contentType')->getData() ?? array_keys($parentLocationsConfig),
95-
),
96-
],
97-
);
98-
99-
$locationQuery->sortClauses = [
100-
new SortClause\ContentName(Query::SORT_ASC),
87+
$criteria = [
88+
new Criterion\ParentLocationId(array_values($parentLocationsConfig)),
89+
new Criterion\Location\IsMainLocation(Criterion\Location\IsMainLocation::MAIN),
90+
new Criterion\ContentTypeIdentifier(
91+
$form->get('contentType')->getData() ?? array_keys($parentLocationsConfig),
92+
),
10193
];
10294

103-
return $locationQuery;
104-
}
95+
if ((bool) $form->get('showOnlyUnused')->getData()) {
96+
$criteria[] = new IsComponentUsed(false);
97+
}
10598

106-
/**
107-
* @return array<string, \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo[]>
108-
*/
109-
private function extractComponents(PagerfantaInterface $pager): array
110-
{
111-
$components = [];
99+
$locationQuery = new LocationQuery();
100+
$locationQuery->filter = new Criterion\LogicalAnd($criteria);
112101

113-
foreach ($pager->getCurrentPageResults() as $location) {
114-
/** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */
115-
$contentTypeName = $location->getContentInfo()->getContentType()->getName();
116-
$components[$contentTypeName][] = $location->getContentInfo();
117-
}
102+
$sortClause = match ($form->get('sortType')->getData()) {
103+
'name' => SortClause\ContentName::class,
104+
'last_modified' => SortClause\DateModified::class,
105+
default => SortClause\ContentName::class,
106+
};
118107

119-
ksort($components);
108+
$locationQuery->sortClauses = [
109+
new $sortClause($form->get('sortDirection')->getData() ?? Query::SORT_ASC),
110+
];
120111

121-
return $components;
112+
return $locationQuery;
122113
}
123114

124115
private function buildPager(AdapterInterface $adapter, Request $request): PagerfantaInterface
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
parameters:
22
netgen_layouts.app.ibexa.item_preview_template: '@@nglayouts_admin/app/item/nglayouts_app_preview.html.twig'
33
netgen_layouts.ibexa.admin.pagelayout: '@@NetgenLayoutsIbexa/admin/pagelayout.html.twig'
4-
netgen_layouts.ibexa.components.limit: 10
4+
netgen_layouts.ibexa.components.limit: 50
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
netgen_layouts.ibexa.search.persistence.criterion_handler.is_component_used:
3+
parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler
4+
class: Netgen\Layouts\Ibexa\Search\Persistence\CriterionHandler\IsComponentUsed
5+
tags:
6+
- { name: ibexa.search.legacy.gateway.criterion_handler.content }
7+
- { name: ibexa.search.legacy.gateway.criterion_handler.location }

bundle/Resources/translations/nglayouts_admin.en.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,20 @@ layout_resolver.rule.target_header.ibexa_semantic_path_info_prefix: 'Applied to
88
menu.main_menu.ibexa.components: 'Components'
99

1010
components.title: 'Components'
11-
components.filter_form.content_type.placeholder: 'Select content type'
11+
components.filter_form.content_type.placeholder: 'All content types'
1212
components.filter_form.content_type.label: 'Content type'
13+
components.filter_form.show_only_unused.label: 'Show only unused'
14+
components.filter_form.sort_type.label: 'Sort type'
15+
components.filter_form.sort_type.name: 'Name'
16+
components.filter_form.sort_type.last_modified: 'Last modified'
17+
components.filter_form.sort_direction.label: 'Sort direction'
18+
components.filter_form.sort_direction.ascending: 'Ascending'
19+
components.filter_form.sort_direction.descending: 'Descending'
1320
components.filter_form.button.submit: 'Submit'
1421
components.no_components: 'No components'
1522
components.not_used: 'Not used in any layout'
23+
components.component.name: 'Name'
24+
components.component.content_type: 'Content type'
25+
components.component.last_modified: 'Last modified'
26+
components.component.usage_count: 'Usage count'
27+
components.component.used_in: 'Used in'

bundle/Resources/views/admin/components/index.html.twig

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,74 @@
1616

1717
<div class="nl-components-head">
1818
<form method="get">
19-
{{ form_row(filter_form.contentType, {placeholder: 'components.filter_form.content_type.placeholder'}) }}
19+
<div class="filter">
20+
{{ form_row(filter_form.contentType, {placeholder: 'components.filter_form.content_type.placeholder'}) }}
21+
{{ form_row(filter_form.showOnlyUnused) }}
22+
</div>
23+
24+
<div class="sort">
25+
{{ form_row(filter_form.sortType) }}
26+
{{ form_row(filter_form.sortDirection) }}
27+
</div>
28+
2029
{{ form_rest(filter_form) }}
2130

2231
<button type="submit">{{ 'components.filter_form.button.submit'|trans }}</button>
2332
</form>
2433
</div>
2534

2635
<div class="nl-components">
27-
{% for component_type, content_list in components %}
28-
<h1>{{ component_type }}</h1>
29-
36+
{% if components|length > 0 %}
3037
<table>
31-
{% for content in content_list %}
32-
{% set has_layouts = component_layouts[content.id] is defined %}
33-
38+
<thead>
3439
<tr>
35-
<td>
36-
<a href="{{ ibexa_path(content) }}" target="_blank">{{ content.name }}</a>
37-
</td>
38-
<td>
39-
{% if has_layouts %}
40-
{% for layout in component_layouts[content.id]['layouts'] %}
41-
<a href="{{ macros.layout_path(layout.uuid) }}" class="js-open-ngl" target="_blank">{{ layout.name }}</a><br>
42-
{% endfor %}
43-
{% else %}
44-
{{ 'components.not_used'|trans }}
45-
{% endif %}
46-
</td>
40+
<th>{{ 'components.component.name'|trans }}</th>
41+
<th>{{ 'components.component.content_type'|trans }}</th>
42+
<th>{{ 'components.component.last_modified'|trans }}</th>
43+
<th>{{ 'components.component.usage_count'|trans }}</th>
44+
<th>{{ 'components.component.used_in'|trans }}</th>
4745
</tr>
48-
{% endfor %}
46+
</thead>
47+
<tbody>
48+
{% for component in components %}
49+
{% set has_layouts = component_layouts[component.content.id] is defined %}
50+
51+
<tr>
52+
<td>
53+
<a href="{{ ibexa_path(component) }}" target="_blank">{{ component.content.name }}</a>
54+
</td>
55+
<td>
56+
{{ component.content.contentType.name }}
57+
</td>
58+
<td>
59+
{{ component.content.contentInfo.modificationDate|nglayouts_format_datetime }}
60+
</td>
61+
<td>
62+
{{ component_layouts[component.content.id].count|default(0) }}
63+
</td>
64+
<td>
65+
{% if has_layouts %}
66+
{% for layout_data in component_layouts[component.content.id]['layouts'] %}
67+
<p>
68+
<a href="{{ macros.layout_path(layout_data.uuid) }}" class="js-open-ngl" target="_blank">{{ layout_data.name }}</a><br>
69+
{{ layout_data.view_types|map(v => v|humanize)|join(', ') }}
70+
</p>
71+
{% endfor %}
72+
{% else %}
73+
{{ 'components.not_used'|trans }}
74+
{% endif %}
75+
</td>
76+
</tr>
77+
{% endfor %}
78+
</tbody>
4979
</table>
5080
{% else %}
5181
<p class="nl-no-items">{{ 'components.no_components'|trans }}</p>
52-
{% endfor %}
82+
{% endif %}
5383

54-
{% if pager.haveToPaginate() %}
84+
{% if components.haveToPaginate() %}
5585
<div class="paging">
56-
{{ pagerfanta(pager) }}
86+
{{ pagerfanta(components) }}
5787
</div>
5888
{% endif %}
5989
</div>

bundle/Resources/views/themes/admin/content/tab/nglayouts/component_layouts.html.twig

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616
<div class="layout-dropdown"></div>
1717
</li>
1818

19-
{% for component_layout in component_layouts %}
20-
{{ nglayouts_render_layout(component_layout, {}, 'ibexa_admin') }}
19+
{% for layout_data in component_layouts %}
20+
{{ nglayouts_render_layout(
21+
layout_data.layout, {
22+
additional_data: layout_data.view_types|map(v => v|humanize)|join(', ')
23+
},
24+
'ibexa_admin'
25+
) }}
2126
{% endfor %}
2227
</ul>
2328
{% else %}

bundle/Resources/views/themes/admin/content/tab/nglayouts/layout.html.twig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
</ul>
2929
</div>
3030
{% endif %}
31+
32+
{% if additional_data|default('') is not empty %}
33+
<div class="additional-data">
34+
{{ additional_data }}
35+
</div>
36+
{% endif %}
3137
</div>
3238

3339
{% if can_clear_cache %}

lib/AdminUI/ComponentLayoutsLoader.php

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,19 @@
99
use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo;
1010
use JsonException;
1111
use Netgen\Layouts\API\Service\LayoutService;
12-
use Netgen\Layouts\API\Values\Layout\Layout;
1312
use Netgen\Layouts\API\Values\Value;
1413
use Ramsey\Uuid\Uuid;
1514

15+
use function array_column;
1616
use function array_key_exists;
17-
use function array_map;
17+
use function array_merge;
18+
use function count;
1819
use function json_decode;
1920

2021
use const JSON_THROW_ON_ERROR;
2122

2223
final class ComponentLayoutsLoader
2324
{
24-
/**
25-
* @var array<int, array<string, array<int, array<string, mixed>>>>
26-
*/
27-
private array $layoutsData;
28-
2925
public function __construct(
3026
private LayoutService $layoutService,
3127
private Connection $databaseConnection,
@@ -34,30 +30,45 @@ public function __construct(
3430
/**
3531
* Returns all layouts in which the provided content is used as a component, sorted by name.
3632
*
37-
* @return iterable<\Netgen\Layouts\API\Values\Layout\Layout>
33+
* @return array<string, array{
34+
* layout?: \Netgen\Layouts\API\Values\Layout\Layout,
35+
* uuid: string,
36+
* name: string,
37+
* view_types: string[]
38+
* }>
3839
*/
3940
public function loadComponentLayouts(ContentInfo $contentInfo): iterable
4041
{
41-
$this->layoutsData ??= $this->loadLayoutsData();
42+
$layoutsData = $this->loadLayoutsData();
4243

43-
if (!array_key_exists($contentInfo->id, $this->layoutsData)) {
44+
if (!array_key_exists($contentInfo->id, $layoutsData)) {
4445
return [];
4546
}
4647

47-
return array_map(
48-
fn (array $layoutData): Layout => $this->layoutService->loadLayout(Uuid::fromString($layoutData['uuid'])),
49-
$this->layoutsData[$contentInfo->id]['layouts'],
50-
);
48+
$componentData = $layoutsData[$contentInfo->id];
49+
50+
foreach ($componentData['layouts'] as $uuid => $layoutData) {
51+
$componentData['layouts'][$uuid]['layout'] = $this->layoutService->loadLayout(Uuid::fromString($uuid));
52+
}
53+
54+
return $componentData['layouts'];
5155
}
5256

5357
/**
54-
* @return array<int, array<string, array<int, array<string, mixed>>>>
58+
* @return array<int, array{
59+
* layouts: array<string, array{
60+
* uuid: string,
61+
* name: string,
62+
* view_types: string[]
63+
* }>,
64+
* count: int
65+
* }>
5566
*/
5667
public function loadLayoutsData(): array
5768
{
5869
$query = $this->databaseConnection->createQueryBuilder();
5970

60-
$query->select('bt.parameters, l.uuid as layout_uuid, l.name as layout_name')
71+
$query->select('b.view_type, bt.parameters, l.uuid as layout_uuid, l.name as layout_name')
6172
->from('nglayouts_block', 'b')
6273
->innerJoin(
6374
'b',
@@ -95,10 +106,17 @@ public function loadLayoutsData(): array
95106
continue;
96107
}
97108

98-
$layoutsData[(int) $decodedParameters['content']]['layouts'][] = [
109+
$layoutsData[(int) $decodedParameters['content']]['layouts'][$dataRow['layout_uuid']] ??= [
99110
'uuid' => $dataRow['layout_uuid'],
100111
'name' => $dataRow['layout_name'],
112+
'view_types' => [],
101113
];
114+
115+
$layoutsData[(int) $decodedParameters['content']]['layouts'][$dataRow['layout_uuid']]['view_types'][] = $dataRow['view_type'];
116+
}
117+
118+
foreach ($layoutsData as $componentId => $componentData) {
119+
$layoutsData[$componentId]['count'] = count(array_merge(...array_column($componentData['layouts'], 'view_types')));
102120
}
103121

104122
return $layoutsData;

0 commit comments

Comments
 (0)