page archives projets filters
This commit is contained in:
@@ -677,8 +677,8 @@ display:
|
|||||||
value: all
|
value: all
|
||||||
title_enable: false
|
title_enable: false
|
||||||
title: All
|
title: All
|
||||||
title_enable: true
|
title_enable: false
|
||||||
title: 'Les projets | {{ arguments.tid }}'
|
title: ''
|
||||||
default_argument_type: fixed
|
default_argument_type: fixed
|
||||||
default_argument_options:
|
default_argument_options:
|
||||||
argument: ''
|
argument: ''
|
||||||
@@ -783,8 +783,25 @@ display:
|
|||||||
arguments: false
|
arguments: false
|
||||||
filters: false
|
filters: false
|
||||||
filter_groups: false
|
filter_groups: false
|
||||||
|
header: false
|
||||||
css_class: 'views-archives views-archives-projets'
|
css_class: 'views-archives views-archives-projets'
|
||||||
display_description: ''
|
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
|
exposed_block: true
|
||||||
display_extenders: { }
|
display_extenders: { }
|
||||||
path: projets
|
path: projets
|
||||||
|
|||||||
@@ -80,3 +80,18 @@ function leshed_helpers_ensure_term_alias(EntityInterface $entity) {
|
|||||||
'langcode' => $langcode,
|
'langcode' => $langcode,
|
||||||
])->save();
|
])->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{
|
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
|
// 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.
|
// seuil mobile. Une dernière rangée incomplète ne s'étire pas.
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -19,6 +46,7 @@ div.views-archives-projets{
|
|||||||
@media #{$mobile}{
|
@media #{$mobile}{
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
article.node-type-projet.view-mode-tableau_liste{
|
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