<?php

/**
 * @file
 * Provides sitemap functionality.
 */

use Drupal\system\Entity\Menu;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Url;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Link;
use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_theme().
 */
function sitemap_theme() {
  return array(
    'sitemap' => array(
      'variables' => array(
        'message' => NULL,
        'rss_legend' => NULL,
        'sitemap_items' => array(),
        'additional' => NULL,
      ),
      'file' => 'sitemap.theme.inc',
    ),
    'sitemap_feed_icon' => array(
      'variables' => array(
        'url' => NULL,
        'name' => NULL,
        'type' => 'node',
      ),
    ),
    'sitemap_menu_link' => array(
      'render element' => 'element',
      'function' => 'theme_sitemap_menu_link',
      'file' => 'sitemap.theme.inc',
    ),
    'sitemap_menu_tree' => array(
      'render element' => 'tree',
      'function' => 'theme_sitemap_menu_tree',
      'file' => 'sitemap.theme.inc',
    ),
  );
}

/**
 * Helper callback for the sitemap front page.
 *
 * @return array
 *   Returns a keyed array with variables for rendering front page sitemap box.
 */
function _sitemap_front_page() {
  $config = \Drupal::config('sitemap.settings');

  $title = t('Front page');
  $content = Link::fromTextAndUrl(t('Front page of %sn', array('%sn' => \Drupal::config('system.site')->get('name'))), Url::fromRoute('<front>', [], array('html' => TRUE)))->toString();
  $attributes = new Attribute();
  $options = array();

  if ($config->get('show_rss_links') != 0) {
    $feed_icon = array(
      '#theme' => 'sitemap_feed_icon',
      '#url' => $config->get('rss_front'),
      '#name' => 'front page',
    );
    $rss_link = \Drupal::service('renderer')->render($feed_icon);

    if ($config->get('show_rss_links') == 1) {
      $content .= ' ' . $rss_link;
    }
    else {
      $attributes->addClass('sitemap-rss-left');
      $content = $rss_link . ' ' . $content;
    }
  }
  \Drupal::service('sitemap.helper')->setOption($options, 'show_titles', 1, 'show_titles', TRUE);

  $attributes->addClass('sitemap-box-front');

  $sitemap_box = array(
    'title' => $title,
    'content' => array('#markup' => $content),
    'attributes' => $attributes,
    'options' => $options,
  );
  return $sitemap_box;
}

/**
 * Helper callback for the sitemap books.
 *
 * @return array
 *   Returns a keyed array with variables for rendering the books sitemap box.
 */
function _sitemap_books() {
  $config = \Drupal::config('sitemap.settings');
  $sitemap_box = array();
  $content = '';
  $options = array();
  $book_titles = array();
  $bids = array_filter($config->get('show_books'));

  if (\Drupal::service('module_handler')->moduleExists('book') && !empty($bids)) {
    $books_expanded = $config->get('books_expanded');
    $title = t('Books');
    $description = '<div class="description">' . t('Books at %sn.', array('%sn' => \Drupal::config('system.site')->get('name'))) . '</div>';

    foreach (\Drupal::service('book.manager')->getAllBooks() as $book) {
      if (in_array($book['bid'], $bids)) {
        if ($books_expanded) {
          // Retrieve the expanded tree.
          // @TODO Ensure the outline is properly translated.
          // @see https://www.drupal.org/node/2615642
          $tree = \Drupal::service('book.manager')->bookTreeAllData($book['bid']);
          $tree_output = \Drupal::service('book.manager')->bookTreeOutput($tree);
          $content .= \Drupal::service('renderer')->render($tree_output);
        }
        else {
          $book_titles[] = Link::fromTextAndUrl($book['title'], Url::fromRoute('entity.node.canonical', array('node' => $book['bid'])))->toString();
        }
      }
    }
    \Drupal::service('sitemap.helper')->setOption($options, 'show_titles', 1, 'show_titles', TRUE);

    if (!$books_expanded && !empty($book_titles)) {
      $book_titles = array(
        '#theme' => 'item_list',
        '#items' => $book_titles,
      );
      $content .= \Drupal::service('renderer')->render($book_titles);
    }

    if (!empty($content)) {
      $attributes = new Attribute();
      $attributes->addClass('sitemap-box-book');

      $sitemap_box = array(
        'title' => $title,
        'content' => array('#markup' => $description . $content),
        'attributes' => $attributes,
        'options' => $options,
      );
    }
  }

  return $sitemap_box;
}

/**
 * Helper callback for the sitemap menus.
 *
 * @return array
 *   Returns a keyed array with variables for rendering the menu's sitemap box.
 */
function _sitemap_menus($mid) {
  $sitemap_box = array();
  $attributes = new Attribute();
  $options = array();

  if (!empty($mid)) {
    $menu = Menu::load($mid);
    // Retrieve the expanded tree.
    $tree = \Drupal::service('menu.link_tree')->load($mid, new MenuTreeParameters());
    $manipulators = array(
      array('callable' => 'menu.default_tree_manipulators:checkAccess'),
      array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
    );
    $tree = \Drupal::service('menu.link_tree')->transform($tree, $manipulators);

    // Add an alter hook so that other modules can manipulate the
    // menu tree prior to rendering.
    $alter_mid = preg_replace('/[^a-z0-9_]+/', '_', $mid);
    \Drupal::moduleHandler()->alter(array('sitemap_menu_tree', 'sitemap_menu_tree_' . $alter_mid), $tree, $menu);

    $menu_display = \Drupal::service('menu.link_tree')->buildForSitemap($tree);
    $menu_html = \Drupal::service('renderer')->render($menu_display);
    if (!empty($menu_html)) {

      // If the menu does not have a special Sitemap display name provided,
      // supply the default menu name.
      if (!empty($menu->getThirdPartySetting('sitemap', 'sitemap_display_name'))) {
        $title = $menu->getThirdPartySetting('sitemap', 'sitemap_display_name');
      } else {
        $title = $menu->label();
      }

      \Drupal::service('sitemap.helper')->setOption($options, 'show_titles', 1, 'show_titles', TRUE);

      $attributes->addClass('sitemap-box-menu', 'sitemap-box-menu-' . $mid);

      $sitemap_box = array(
        'title' => $title,
        'content' => $menu_html,
        'attributes' => $attributes,
        'options' => $options,
      );
    }
  }

  return $sitemap_box;
}

/**
 * Count the number of published nodes classified by a term.
 *
 * This is a re-implementation of taxonomy_term_count_nodes() that has been
 * removed from D7 core.
 *
 * Implementation note: the normal way to count field instances is through
 * field_attach_query(), but taxonomy.module has a special denormalized
 * table taxonomy_index which we can use for more speed. THX to taxonews.
 *
 * @param string $tid
 *   The term's ID.
 *
 * @return string
 *   An integer representing a number of nodes. Results are statically cached.
 */
function sitemap_taxonomy_term_count_nodes($tid) {
  $query = db_select('taxonomy_index', 'ti');
  $query->addExpression('COUNT(ti.nid)');
  $count = $query
    ->condition('ti.tid', $tid)
    ->execute()->fetchCol();
  return $count[0];
}

/**
 * Implements hook_form_alter().
 */
function sitemap_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Apply the same form_alter both the add and edit forms.
  if ($form_id === 'menu_add_form' || $form_id === 'menu_edit_form') {
    sitemap_alter_menu_forms($form, $form_state, $form_id);
  }
}

/**
 * Handles the form alter for the menu_add_form and menu_edit_form forms to add
 * a settings field to the menu entity.
 */
function sitemap_alter_menu_forms(&$form, FormStateInterface $form_state, $form_id) {
  $menu = $form_state->getFormObject()->getEntity();
  $form['sitemap_display_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Sitemap display name'),
    '#description' => t('A user-friendly name for the menu that will display on the sitemap.'),
    '#default_value' => $menu->getThirdPartySetting('sitemap', 'sitemap_display_name'),
    '#weight' => 1
  );

  if (isset($form['links'])) {
    $form['links']['#weight'] = 2;
  }

  $form['#entity_builders'][] = 'sitemap_form_menu_add_form_builder';
}

/**
 * Entity builder for the menu configuration entity.
 */
function sitemap_form_menu_add_form_builder($entity_type, \Drupal\system\Entity\Menu $menu, &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  if ($form_state->getValue('sitemap_display_name')) {
    $menu->setThirdPartySetting('sitemap', 'sitemap_display_name', $form_state->getValue('sitemap_display_name'));
    return;
  }
}