You are here

archive.module in Archive 7.2

Same filename and directory in other branches
  1. 5 archive.module
  2. 6 archive.module
  3. 7 archive.module

Implements a block and page showing an archive of all site content.

File

archive.module
View source
<?php

/**
 * @file
 * Implements a block and page showing an archive of all site content.
 */

/**
 * Implementation of hook_help().
 */
function archive_help($path, $arg) {
  switch ($path) {
    case 'admin/help#archive':
      $output = '<p>' . t('The archive module allows your users to browse your content by its type and date of publication. The archive module interface, available at the path !archive_link, presents simple controls for selecting a year, month or day. All available posts published within this range are displayed beneath the control. Administrators may select the content types to be displayed. The archive module adds an <em>Archives</em> menu item to the default <em>Navigation</em> menu (the item is disabled by default), and displays the current month and year within an optional block.', array(
        '!archive_link' => l(t('archive'), 'archive'),
      )) . '</p>';
      $output .= '<p>' . t('You can') . '</p>';
      $output .= theme('item_list', array(
        'items' => array(
          t('view your !archive_link.', array(
            '!archive_link' => l(t('Archive'), 'archive'),
          )),
          t('enable, position and configure the <em>Current calendar month with archive links</em> block at the !blocks.', array(
            '!blocks' => l(t('blocks administration page'), 'admin/build/block'),
          )),
          t('enable or disable the <em>Archives</em> menu item on the !menu-administration.', array(
            '!menu-administration' => l(t('navigation menu administration page'), 'admin/build/menu-customize/navigation'),
          )),
        ),
      ));
      $output .= '<p>' . t('For more information, see the online handbook entry for !handbook-page.', array(
        '!handbook-page' => l(t('Archive module'), 'http://drupal.org/handbook/modules/archive'),
      )) . '</p>';
      return $output;
  }
}

/**
 * Implementation of hook_menu().
 */
function archive_menu() {
  $items = array();
  $items['archive'] = array(
    'title' => 'Archives',
    'access arguments' => array(
      'access content',
    ),
    'page callback' => 'archive_page',
    'file' => 'archive.pages.inc',
    'type' => MENU_SUGGESTED_ITEM,
  );
  $items['admin/config/user-interface/archive'] = array(
    'title' => 'Archives',
    'description' => 'Select the content types listed in the archives.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'archive_admin_settings',
    ),
    'access arguments' => array(
      'access administration pages',
    ),
    'file' => 'archive.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Implementation of hook_uninstall().
 */
function archive_uninstall() {
  variable_del('archive_type_filters');
}

/**
 * Implementation of hook_theme().
 */
function archive_theme() {
  return array(
    'archive_block_calendar' => array(
      'variables' => array(
        'timestamp' => 0,
      ),
    ),
    'archive_page_title' => array(
      'variables' => array(
        'type' => '',
        'year' => NULL,
        'month' => NULL,
        'day' => NULL,
      ),
    ),
    'archive_navigation' => array(
      'variables' => array(
        'type' => '',
        'date' => NULL,
      ),
    ),
    'archive_navigation_days' => array(
      'variables' => array(
        'type' => '',
        'date' => NULL,
      ),
    ),
    'archive_navigation_months' => array(
      'variables' => array(
        'type' => '',
        'date' => NULL,
      ),
    ),
    'archive_navigation_node_types' => array(
      'variables' => array(
        'type' => '',
        'date' => NULL,
      ),
    ),
    'archive_navigation_years' => array(
      'variables' => array(
        'type' => '',
        'date' => NULL,
      ),
    ),
    'archive_separator' => array(
      'variables' => array(
        'date_created' => 0,
        'separators' => array(),
      ),
    ),
  );
}

/**
 * Returns a single month as a calendar grid.
 *
 * @todo
 *   Take the archive logic out to allow better theme-overloading
 *   of this function.
 */
function theme_archive_block_calendar($variables) {
  $the_date = explode(' ', format_date($variables['timestamp'], 'custom', 'F Y n t'));
  $title = $the_date[0] . ' ' . $the_date[1];
  $year = $the_date[1];
  $month = $the_date[2];
  $num_days = (int) $the_date[3];

  // Get the number of days for the previous month for adding those dates
  // to the block calendar. Store the month/year for the previous month
  // so that we can find and link to archive pages in those extra days.
  // We don't need to get days for the next month as we'll never need to fit
  // the entire month into the block.
  $prev_month = $month == 1 ? 12 : $month - 1;
  $prev_year = $month == 1 ? $year - 1 : $year;
  $num_days_prev = cal_days_in_month(CAL_GREGORIAN, $month - 1, $year);
  $next_month = $month == 12 ? 1 : $month + 1;
  $next_year = $month == 12 ? $year + 1 : $year;
  $date = _archive_date('all', $year, $month);
  $month_title = '';
  if (isset($date->months[$month])) {
    $month_title = l($title, _archive_url('all', $year, $month), array(
      'title' => format_plural($date->months[format_date($variables['timestamp'], 'custom', 'n')], '1 post', '@count posts'),
    ));
  }
  else {
    $month_title = $title;
  }

  // Build the week starting with
  $first_day_of_week = variable_get('date_first_day', 0);
  $week = array(
    t('Sun'),
    t('Mon'),
    t('Tue'),
    t('Wed'),
    t('Thu'),
    t('Fri'),
    t('Sat'),
  );
  $day_headers = array();
  for ($i = $first_day_of_week; $i < $first_day_of_week + 7; $i++) {
    $day_headers[] = $week[$i % 7];
  }

  // Grab the first day of the month using the user's timezone
  list($start_year, $start_month) = explode(' ', format_date($variables['timestamp'], 'custom', 'Y m'));
  $start = gmmktime(0, 0, 0, (int) $start_month, 1, (int) $start_year);
  $weekday = gmdate('w', $start) - $first_day_of_week;
  $days_row = array();

  // From http://www.theadminzone.com/forums/showthread.php?t=17490
  for ($i = 1 - $weekday; $i <= ceil(($weekday + $num_days) / 7) * 7; $i++) {
    if ($i > 0) {
      if (array_key_exists($i, $date->days)) {
        $days_row[] = l($i, _archive_url('all', $year, $month, $i), array(
          'attributes' => array(
            'title' => format_plural($date->days[$i], '1 post', '@count posts'),
          ),
        ));
      }
      elseif ($i <= $num_days) {
        $days_row[] = $i;
      }
      else {
        $curr_cal_date = $i - $num_days;
        if (isset($date->next_month_days[$curr_cal_date])) {
          $data = l($curr_cal_date, _archive_url('all', $next_year, $next_month, $curr_cal_date), array(
            'attributes' => array(
              'title' => format_plural($date->next_month_days[$curr_cal_date], '1 post', '@count posts'),
            ),
          ));
        }
        else {
          $data = $curr_cal_date;
        }
        $days_row[] = array(
          'data' => $data,
          'class' => 'out-of-month',
        );
      }

      // Add the week table row we just created if we've finished it
      if (($i + $weekday) % 7 == 0) {
        $rows[] = $days_row;
        $days_row = array();
      }
    }
    else {
      $curr_cal_date = $num_days_prev + $i;
      if (isset($date->prev_month_days[$curr_cal_date])) {
        $data = l($curr_cal_date, _archive_url('all', $prev_year, $prev_month, $curr_cal_date), array(
          'attributes' => array(
            'title' => format_plural($date->prev_month_days[$curr_cal_date], '1 post', '@count posts'),
          ),
        ));
      }
      else {
        $data = $curr_cal_date;
      }
      $days_row[] = array(
        'data' => $data,
        'class' => 'out-of-month',
      );
    }
  }
  return theme('table', array(
    'header' => $day_headers,
    'rows' => $rows,
    'caption' => $month_title,
  ));
}

/**
 * Implements hook_block().
 */
function archive_block_info() {
  $blocks = array();
  $blocks['month_archive']['info'] = t('Current calendar month with archive links');
  return $blocks;
}

/*
 * Implements hook_block_view().
 */
function archive_block_view($delta = NULL) {
  drupal_add_css(drupal_get_path('module', 'archive') . '/archive.css');
  $block = array(
    'subject' => t('Archives'),
    'content' => theme('archive_block_calendar', array(
      'timestamp' => time(),
    )),
  );
  return $block;
}

/**
 * Generate an archive URL based on the $y, $m and $d provided.
 *
 * @param $type
 *   The node type to use if valid.
 * @param $y
 *   The year to use if valid.
 * @param $m
 *   The month to use if valid.
 * @param $d
 *   The day to use if valid.
 * @return
 *   A string with the generated archive URL.
 */
function _archive_url($type, $y = 0, $m = 0, $d = 0) {
  $url = 'archive';
  if (_archive_validate_type($type)) {
    $url .= '/' . $type;
  }
  else {
    $url .= '/all';
  }
  if (_archive_validate_date($y, $m, $d)) {
    $url .= '/' . $y . '/' . $m . '/' . $d;
  }
  elseif (_archive_validate_date($y, $m)) {
    $url .= '/' . $y . '/' . $m;
  }
  elseif (_archive_validate_date($y)) {
    $url .= '/' . $y;
  }
  return $url;
}

/**
 * Parses the current URL and populates an archive date object.
 *
 * @param $year
 *   Number of year.
 * @param $month
 *   Number of month.
 * @param $day
 *   Number of day.
 * @return
 *   A date object with GMT date values and a timezone value.
 */
function _archive_date($type, $year = 0, $month = 0, $day = 0) {
  $date = new stdClass();
  $date->tz = _archive_get_timezone();
  $date->year = 0;
  $date->month = 0;
  $date->day = 0;
  if (_archive_validate_date($year, $month, $day)) {
    $date->year = $year;
    $date->month = $month;
    $date->day = $day;
  }
  elseif (_archive_validate_date($year, $month)) {
    $date->year = $year;
    $date->month = $month;
  }
  elseif (_archive_validate_date($year)) {
    $date->year = $year;
  }
  $post_counts = _archive_post_count($type, $date);
  $date->years = $post_counts['years'];
  ksort($date->years);
  $date->months = $post_counts['months'];
  ksort($date->months);
  $date->days = $post_counts['days'];
  ksort($date->days);
  $date->next_month_days = $post_counts['next_month_days'];
  $date->prev_month_days = $post_counts['prev_month_days'];
  return $date;
}

/**
 * Determine timezone to use for the dates (from format_date).
 *
 * @return
 *   Timezone offset to use in time operations.
 */
function _archive_get_timezone() {
  global $user;
  if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) {
    return $user->timezone;
  }
  else {
    return variable_get('date_default_timezone', 0);
  }
}
function _archive_types_sql_array($type) {

  // Validate type and specify node types to include
  $final_types = array();
  if (_archive_validate_type($type) && $type != 'all') {
    $final_types[] = $type;
  }
  else {
    $types = variable_get('archive_type_filters', array());

    // If no checkboxes selected
    if (!array_key_exists('0', $types)) {
      foreach ($types as $key => $value) {
        if (!$value) {
          unset($types[$key]);
        }
      }
    }
    $final_types = $types;
  }
  return $final_types;
}

/**
 * Check if given year, month and date combination is valid for the archive.
 *
 * @param $year
 *   The year to check.
 * @param $month
 *   The month to check.
 * @param $day
 *   The day to check.
 * @return
 *   TRUE or FALSE.
 */
function _archive_validate_date($year, $month = NULL, $day = NULL) {
  $valid = FALSE;
  if ($year && $year <= format_date(time(), 'custom', 'Y')) {
    if (!is_null($month)) {
      if (!is_null($day)) {
        if ($last = gmdate('t', gmmktime(0, 0, 0, $month, 1, $year))) {
          $valid = 0 < $day && $day <= $last;
        }
      }
      elseif (0 < $month && $month <= 12) {
        $valid = TRUE;
      }
    }
    else {
      $valid = TRUE;
    }
  }
  return $valid;
}

/**
 * Check if given node type is valid for the archive.
 *
 * @param $type
 *   The type to check.
 * @return
 *   TRUE or FALSE.
 */
function _archive_validate_type($type) {
  $types = variable_get('archive_type_filters', array());
  return in_array($type, $types);
}

/**
 * Returns the range of dates with nodes.
 *
 * Returns an array with keys of 'years', 'months', and 'days' which map
 * their respective timeframe to the number of posts in that timeframe.
 *
 * @param $type
 *   The object that is being filtered.
 * @param $date
 *   A date object returned from _archive_date().
 * @return
 *   An array of the (first year with posts, last year with posts).
 */
function _archive_post_count($type, $date) {
  $final_types = _archive_types_sql_array($type);
  $node_query = db_select('node', 'n');
  $node_query
    ->fields('n', array(
    'uid',
    'created',
  ));
  $node_query
    ->condition('n.status', 1);
  $node_query
    ->condition('n.type', $final_types, 'IN');
  $node_query
    ->addTag('node_access');
  $result = $node_query
    ->execute();
  $with_posts = array(
    'years' => array(),
    'months' => array(),
    'days' => array(),
    'next_month_days' => array(),
    'prev_month_days' => array(),
  );
  $next_month = $date->month == 12 ? 1 : $date->month + 1;
  $next_year = $date->month == 12 ? $date->year + 1 : $date->year;
  $prev_month = $date->month == 1 ? 12 : $date->month - 1;
  $prev_year = $date->month == 1 ? $date->year - 1 : $date->year;
  foreach ($result as $o) {
    list($year, $month, $day) = explode(' ', format_date($o->created, 'custom', 'Y n j'));

    // Check for current month.
    $with_posts['years'][$year] = array_key_exists($year, $with_posts['years']) ? $with_posts['years'][$year] + 1 : 1;
    if ($date->year && $year == $date->year) {
      $with_posts['months'][$month] = array_key_exists($month, $with_posts['months']) ? $with_posts['months'][$month] + 1 : 1;
      if ($date->month && $month == $date->month) {
        $with_posts['days'][$day] = array_key_exists($day, $with_posts['days']) ? $with_posts['days'][$day] + 1 : 1;
      }
    }

    // Check for previous month.
    if ($year == $next_year && $month == $next_month) {
      if (isset($with_posts['next_month_days'][$day])) {
        $with_posts['next_month_days'][$day]++;
      }
      else {
        $with_posts['next_month_days'][$day] = 1;
      }
    }

    // Check for next month.
    if ($year == $prev_year && $month == $prev_month) {
      if (isset($with_posts['prev_month_days'][$day])) {
        $with_posts['prev_month_days'][$day]++;
      }
      else {
        $with_posts['prev_month_days'][$day] = 1;
      }
    }
  }
  return $with_posts;
}

Functions

Namesort descending Description
archive_block_info Implements hook_block().
archive_block_view
archive_help Implementation of hook_help().
archive_menu Implementation of hook_menu().
archive_theme Implementation of hook_theme().
archive_uninstall Implementation of hook_uninstall().
theme_archive_block_calendar Returns a single month as a calendar grid.
_archive_date Parses the current URL and populates an archive date object.
_archive_get_timezone Determine timezone to use for the dates (from format_date).
_archive_post_count Returns the range of dates with nodes.
_archive_types_sql_array
_archive_url Generate an archive URL based on the $y, $m and $d provided.
_archive_validate_date Check if given year, month and date combination is valid for the archive.
_archive_validate_type Check if given node type is valid for the archive.