page archives projets filters
This commit is contained in:
@@ -677,8 +677,8 @@ display:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: true
|
||||
title: 'Les projets | {{ arguments.tid }}'
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
@@ -783,8 +783,25 @@ display:
|
||||
arguments: false
|
||||
filters: false
|
||||
filter_groups: false
|
||||
header: false
|
||||
css_class: 'views-archives views-archives-projets'
|
||||
display_description: ''
|
||||
header:
|
||||
leshed_helpers_term_links:
|
||||
id: leshed_helpers_term_links
|
||||
table: views
|
||||
field: leshed_helpers_term_links
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
plugin_id: leshed_helpers_term_links
|
||||
empty: true
|
||||
vocabulary: type_de_projet
|
||||
base_path: projets
|
||||
list_title: 'type de projet'
|
||||
show_all: true
|
||||
only_used: true
|
||||
used_bundle: projet
|
||||
exposed_block: true
|
||||
display_extenders: { }
|
||||
path: projets
|
||||
|
||||
@@ -80,3 +80,18 @@ function leshed_helpers_ensure_term_alias(EntityInterface $entity) {
|
||||
'langcode' => $langcode,
|
||||
])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Déclare le plugin d'area "liste de termes en liens" pour les vues.
|
||||
*/
|
||||
function leshed_helpers_views_data() {
|
||||
$data['views']['leshed_helpers_term_links'] = [
|
||||
'title' => t('Liste de termes en liens (Le Shed)'),
|
||||
'help' => t("Liste les termes d'un vocabulaire en liens vers un chemin de base suivi du tid."),
|
||||
'area' => [
|
||||
'id' => 'leshed_helpers_term_links',
|
||||
],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Plugin d'area Views listant les termes d'un vocabulaire en liens.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\leshed_helpers\Plugin\views\area;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\Attribute\ViewsArea;
|
||||
use Drupal\views\Plugin\views\area\AreaPluginBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Affiche les termes d'un vocabulaire en liens vers un chemin filtré par tid.
|
||||
*
|
||||
* Chaque terme pointe vers <base_path>/<tid> ; l'alias géré par
|
||||
* leshed_helpers (ou pathauto) est appliqué automatiquement à la génération
|
||||
* du lien.
|
||||
*/
|
||||
#[ViewsArea('leshed_helpers_term_links')]
|
||||
class TermLinks extends AreaPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Le gestionnaire d'entités.
|
||||
*/
|
||||
protected EntityTypeManagerInterface $entityTypeManager;
|
||||
|
||||
/**
|
||||
* La connexion à la base de données.
|
||||
*/
|
||||
protected Connection $connection;
|
||||
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, Connection $connection) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['vocabulary'] = ['default' => ''];
|
||||
$options['base_path'] = ['default' => ''];
|
||||
$options['list_title'] = ['default' => ''];
|
||||
$options['show_all'] = ['default' => TRUE];
|
||||
$options['only_used'] = ['default' => FALSE];
|
||||
$options['used_bundle'] = ['default' => ''];
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
|
||||
$vocabularies = [];
|
||||
foreach ($this->entityTypeManager->getStorage('taxonomy_vocabulary')->loadMultiple() as $vocabulary) {
|
||||
$vocabularies[$vocabulary->id()] = $vocabulary->label();
|
||||
}
|
||||
|
||||
$form['vocabulary'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Vocabulaire'),
|
||||
'#options' => $vocabularies,
|
||||
'#default_value' => $this->options['vocabulary'],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['base_path'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Chemin de base'),
|
||||
'#description' => $this->t('Chemin de la vue filtrée, sans slash initial. Exemple : projets.'),
|
||||
'#default_value' => $this->options['base_path'],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['list_title'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Titre de la liste'),
|
||||
'#default_value' => $this->options['list_title'],
|
||||
];
|
||||
$form['show_all'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Ajouter un lien « Tous » vers la page non filtrée'),
|
||||
'#default_value' => $this->options['show_all'],
|
||||
];
|
||||
$form['only_used'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t("N'afficher que les termes utilisés par au moins un contenu publié"),
|
||||
'#default_value' => $this->options['only_used'],
|
||||
];
|
||||
$node_types = [];
|
||||
foreach ($this->entityTypeManager->getStorage('node_type')->loadMultiple() as $node_type) {
|
||||
$node_types[$node_type->id()] = $node_type->label();
|
||||
}
|
||||
$form['used_bundle'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Type de contenu'),
|
||||
'#options' => $node_types,
|
||||
'#empty_option' => $this->t('- Tous types de contenu -'),
|
||||
'#empty_value' => '',
|
||||
'#default_value' => $this->options['used_bundle'],
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="options[only_used]"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($empty = FALSE) {
|
||||
if (empty($this->options['vocabulary']) || empty($this->options['base_path'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$storage = $this->entityTypeManager->getStorage('taxonomy_term');
|
||||
$tree = $storage->loadTree($this->options['vocabulary']);
|
||||
if (empty($tree)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// tids des termes du vocabulaire, dans l'ordre de l'arborescence.
|
||||
$tids = array_map(static fn($term) => (int) $term->tid, $tree);
|
||||
|
||||
if (!empty($this->options['only_used'])) {
|
||||
// taxonomy_index ne référence que les contenus publiés ; jointure sur
|
||||
// node_field_data pour restreindre au type de contenu voulu. Une seule
|
||||
// requête pour tout le vocabulaire.
|
||||
$query = $this->connection->select('taxonomy_index', 'ti');
|
||||
$query->distinct();
|
||||
$query->fields('ti', ['tid']);
|
||||
$query->condition('ti.tid', $tids, 'IN');
|
||||
$query->join('node_field_data', 'nfd', 'nfd.nid = ti.nid AND nfd.status = 1');
|
||||
if (!empty($this->options['used_bundle'])) {
|
||||
$query->condition('nfd.type', $this->options['used_bundle']);
|
||||
}
|
||||
$used = array_map('intval', $query->execute()->fetchCol());
|
||||
$tids = array_values(array_intersect($tids, $used));
|
||||
}
|
||||
|
||||
$entities = $storage->loadMultiple($tids);
|
||||
|
||||
// tids présents dans les arguments de la vue, pour marquer le lien actif.
|
||||
$current_tids = [];
|
||||
foreach ($this->view->args as $arg) {
|
||||
foreach (explode(',', (string) $arg) as $tid) {
|
||||
$current_tids[] = trim($tid);
|
||||
}
|
||||
}
|
||||
|
||||
$base_path = '/' . trim($this->options['base_path'], '/');
|
||||
$items = [];
|
||||
|
||||
if (!empty($this->options['show_all'])) {
|
||||
$items[] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Tous'),
|
||||
'#url' => Url::fromUserInput($base_path),
|
||||
'#attributes' => $current_tids ? [] : ['class' => ['is-active']],
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($tree as $position) {
|
||||
$term = $entities[(int) $position->tid] ?? NULL;
|
||||
if ($term === NULL || !$term->isPublished()) {
|
||||
continue;
|
||||
}
|
||||
$items[] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $term->label(),
|
||||
'#url' => Url::fromUserInput($base_path . '/' . $term->id()),
|
||||
'#attributes' => in_array((string) $term->id(), $current_tids, TRUE)
|
||||
? ['class' => ['is-active']]
|
||||
: [],
|
||||
];
|
||||
}
|
||||
|
||||
$cache_tags = ['taxonomy_term_list:' . $this->options['vocabulary']];
|
||||
if (!empty($this->options['only_used'])) {
|
||||
// Le filtrage par usage dépend des contenus publiés.
|
||||
$cache_tags[] = 'node_list';
|
||||
}
|
||||
|
||||
return [
|
||||
'#theme' => 'item_list',
|
||||
'#title' => $this->options['list_title'],
|
||||
'#list_type' => 'ul',
|
||||
'#items' => $items,
|
||||
'#attributes' => ['class' => ['leshed-term-links']],
|
||||
'#cache' => [
|
||||
'tags' => $cache_tags,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -11,6 +11,33 @@
|
||||
|
||||
|
||||
div.views-archives-projets{
|
||||
// Le header (liste « type de projet ») vit hors de la grille :
|
||||
>header{
|
||||
padding-bottom: 2em;
|
||||
|
||||
h3{
|
||||
font-size: 1em;
|
||||
margin:0 0 0.5em 0;
|
||||
font-weight: 400;
|
||||
// font-size: ;
|
||||
}
|
||||
ul{
|
||||
padding:0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em;
|
||||
li{
|
||||
list-style: none;
|
||||
line-height: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// les rows sont wrappées dans div.view-content (template
|
||||
// views-view--archives--page-1.html.twig), qui porte la grille.
|
||||
div.view-content{
|
||||
// Design : 4 colonnes ou 2, jamais 3 — grille explicite qui bascule au
|
||||
// seuil mobile. Une dernière rangée incomplète ne s'étire pas.
|
||||
display: grid;
|
||||
@@ -19,6 +46,7 @@ div.views-archives-projets{
|
||||
@media #{$mobile}{
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
article.node-type-projet.view-mode-tableau_liste{
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
{#
|
||||
/**
|
||||
* @file
|
||||
* Theme override for main view template — page Projets de la vue Archives.
|
||||
*
|
||||
* Les rows sont wrappées dans un div.view-content pour que le header
|
||||
* (liste « type de projet ») ne soit pas un enfant direct de la grille.
|
||||
*
|
||||
* Available variables:
|
||||
* - attributes: Remaining HTML attributes for the element.
|
||||
* - css_name: A CSS-safe version of the view name.
|
||||
* - css_class: The user-specified classes names, if any.
|
||||
* - header: The optional header.
|
||||
* - footer: The optional footer.
|
||||
* - rows: The results of the view query, if any.
|
||||
* - empty: The content to display if there are no rows.
|
||||
* - pager: The optional pager next/prev links to display.
|
||||
* - exposed: Exposed widget form/info to display.
|
||||
* - feed_icons: Optional feed icons to display.
|
||||
* - more: An optional link to the next page of results.
|
||||
* - title: Title of the view, only used when displaying in the admin preview.
|
||||
* - title_prefix: Additional output populated by modules, intended to be
|
||||
* displayed in front of the view title.
|
||||
* - title_suffix: Additional output populated by modules, intended to be
|
||||
* displayed after the view title.
|
||||
* - attachment_before: An optional attachment view to be displayed before the
|
||||
* view content.
|
||||
* - attachment_after: An optional attachment view to be displayed after the
|
||||
* view content.
|
||||
* - dom_id: Unique id for every view being printed to give unique class for
|
||||
* Javascript.
|
||||
*
|
||||
* @see template_preprocess_views_view()
|
||||
*/
|
||||
#}
|
||||
{%
|
||||
set classes = [
|
||||
dom_id ? 'js-view-dom-id-' ~ dom_id,
|
||||
]
|
||||
%}
|
||||
<div{{ attributes.addClass(classes) }}>
|
||||
{{ title_prefix }}
|
||||
{{ title }}
|
||||
{{ title_suffix }}
|
||||
|
||||
{% if header %}
|
||||
<header>
|
||||
{{ header }}
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
{{ exposed }}
|
||||
{{ attachment_before }}
|
||||
|
||||
{% if rows -%}
|
||||
<div class="view-content">
|
||||
{{ rows }}
|
||||
</div>
|
||||
{% elseif empty -%}
|
||||
{{ empty }}
|
||||
{% endif %}
|
||||
{{ pager }}
|
||||
|
||||
{{ attachment_after }}
|
||||
{{ more }}
|
||||
|
||||
{% if footer %}
|
||||
<footer>
|
||||
{{ footer }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
|
||||
{{ feed_icons }}
|
||||
</div>
|
||||
Reference in New Issue
Block a user