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 .= '';
$output .= '' . $year . ' (' . $data['count'] . ')';
$output .= '
';
if (!empty($data['months'])) {
foreach ($data['months'] as $month => $month_data) {
$month_url = '/archive-tree/' . $year . '/' . $month . '/' . $type_arg;
$output .= '