147 lines
4.7 KiB
PHP
147 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace Drupal\archive_tree\Plugin\Block;
|
|
|
|
use Drupal\Core\Block\Attribute\Block;
|
|
use Drupal\Core\Block\BlockBase;
|
|
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
|
use Drupal\Core\Form\FormStateInterface;
|
|
use Drupal\node\Entity\NodeType;
|
|
|
|
|
|
/**
|
|
* Provides an 'Archive tree' block.
|
|
*/
|
|
#[Block(
|
|
id: "archive_tree",
|
|
admin_label: new TranslatableMarkup("Archive tree"),
|
|
category: new TranslatableMarkup("Menus")
|
|
)]
|
|
|
|
class ArchiveTreeBlock extends BlockBase {
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
|
|
public function defaultConfiguration() {
|
|
return [
|
|
'expand_years' => FALSE,
|
|
'content_types' => [],
|
|
] + parent::defaultConfiguration();
|
|
}
|
|
|
|
|
|
public function blockForm($form, FormStateInterface $form_state) {
|
|
$form = parent::blockForm($form, $form_state);
|
|
$types = NodeType::loadMultiple();
|
|
$options = [];
|
|
foreach ($types as $type) {
|
|
$options[$type->id()] = $type->label();
|
|
}
|
|
$form['content_types'] = [
|
|
'#type' => 'checkboxes',
|
|
'#title' => $this->t('Content types to include'),
|
|
'#options' => $options,
|
|
'#default_value' => isset($this->configuration['content_types']) ? $this->configuration['content_types'] : [],
|
|
'#description' => $this->t('Select one or more content types to include in the archive tree.'),
|
|
'#required' => TRUE,
|
|
];
|
|
$form['expand_years'] = [
|
|
'#type' => 'checkbox',
|
|
'#title' => $this->t('Expand all years by default'),
|
|
'#default_value' => $this->configuration['expand_years'],
|
|
'#description' => $this->t('If checked, all years will be expanded by default.'),
|
|
];
|
|
return $form;
|
|
}
|
|
|
|
|
|
public function blockSubmit($form, FormStateInterface $form_state) {
|
|
parent::blockSubmit($form, $form_state);
|
|
$this->configuration['expand_years'] = $form_state->getValue('expand_years');
|
|
$selected_types = array_keys(array_filter($form_state->getValue('content_types')));
|
|
$this->configuration['content_types'] = $selected_types;
|
|
}
|
|
|
|
public function build(): array {
|
|
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
|
$types = array_filter($this->configuration['content_types']);
|
|
if (empty($types)) {
|
|
// If no types selected, return empty block.
|
|
return [
|
|
'#markup' => $this->t('No content types selected.'),
|
|
];
|
|
}
|
|
// 1. Get node IDs with access check.
|
|
$query = $storage->getQuery()
|
|
->condition('type', $types, 'IN')
|
|
->condition('status', 1)
|
|
->sort('created', 'DESC')
|
|
->accessCheck(TRUE);
|
|
$nids = $query->execute();
|
|
|
|
$tree = [];
|
|
if (!empty($nids)) {
|
|
// 2. Fetch only nid and created fields for those nodes.
|
|
$connection = \Drupal::database();
|
|
$result = $connection->select('node_field_data', 'n')
|
|
->fields('n', ['nid', 'created'])
|
|
->condition('n.nid', $nids, 'IN')
|
|
->execute();
|
|
foreach ($result as $row) {
|
|
$created = $row->created;
|
|
$year = date('Y', $created);
|
|
$month = date('m', $created);
|
|
if (!isset($tree[$year])) {
|
|
$tree[$year] = ['count' => 0, 'months' => []];
|
|
}
|
|
if (!isset($tree[$year]['months'][$month])) {
|
|
$tree[$year]['months'][$month] = ['count' => 0];
|
|
}
|
|
$tree[$year]['count']++;
|
|
$tree[$year]['months'][$month]['count']++;
|
|
}
|
|
}
|
|
|
|
krsort($tree); // Descending years
|
|
foreach ($tree as $year => $data) {
|
|
krsort($tree[$year]['months']); // Descending months
|
|
}
|
|
|
|
$output = '';
|
|
$expand = !empty($this->configuration['expand_years']);
|
|
$type_arg = implode(',', $types);
|
|
foreach ($tree as $year => $data) {
|
|
$year_url = '/archive-tree/' . $year . '/' . $type_arg;
|
|
$open = $expand ? ' open' : '';
|
|
$output .= '<details' . $open . '><summary class="archive-tree-year">';
|
|
$output .= '<a href="' . $year_url . '">' . $year . '</a> (' . $data['count'] . ')';
|
|
$output .= '</summary>';
|
|
if (!empty($data['months'])) {
|
|
foreach ($data['months'] as $month => $month_data) {
|
|
$month_url = '/archive-tree/' . $year . '/' . $month . '/' . $type_arg;
|
|
$output .= '<div class="archive-tree-month">';
|
|
$output .= '<a href="' . $month_url . '">' . $month . '</a> (' . $month_data['count'] . ')';
|
|
$output .= '</div>';
|
|
}
|
|
}
|
|
$output .= '</details>';
|
|
}
|
|
|
|
return [
|
|
'#markup' => '<div class="archive-tree-block">' . $output . '</div>',
|
|
'#allowed_tags' => ['details', 'summary', 'a', 'div'],
|
|
'#attached' => [
|
|
'library' => [
|
|
'archive_tree/archive_tree',
|
|
],
|
|
],
|
|
'#cache' => [
|
|
'contexts' => ['user', 'languages'],
|
|
'tags' => ['node_list'],
|
|
'max-age' => -1,
|
|
],
|
|
];
|
|
}
|
|
}
|