<?php
// $Id: archive.module,v 1.1 2005/04/17 21:31:41 codemonkeyx Exp $

/**
 * @file
 * Displays a calendar to navigate older content.
 */
 
/**
 * Implementation of hook_help().
 */
function archive_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Displays a calendar for navigating older content.');
  }
}

/**
 * Implementation of hook_menu().
 */
function archive_menu($may_cache) {
  $items = array();

  if ($may_cache) {
    $items[] = array('path' => 'archive', 'title' => t('archives'),
      'callback' => 'archive_page', 'access' => user_access('access content'));
  }

  return $items;
}

/***
 * Parses the current URL and populates an archive date object with the selected date information.
 ***/
function _archive_selected_date() {
  global $user;
  static $arch_data = null;

  // Implement a crude data cache for multiple calls to this function.
  if ($arch_data != null) {
    return $arch_data;
  }

  $arch_data->today = time() + $user->timezone;

  // Initialize the archive data object.
  $arch_data->year       = 0;
  $arch_data->month      = 0;
  $arch_data->day        = 0;

  if (archive_validYear(arg(1))) {
    $arch_data->year = arg(1);

    if (archive_validMonth(arg(2))) {
      $arch_data->month = arg(2);

      if (archive_validDay(arg(3), arg(2), arg(1))) {
        $arch_data->day = arg(3);
      }
    }
  }
  else {
    $arch_data->year = date("Y", $arch_data->today);
  }

  $arch_data->cur_date      = mktime(0, 0, 0, $arch_data->month, $arch_data->day, $arch_data->year) - $user->timezone;
  $arch_data->cur_date_end  = mktime(0, 0, 0, $arch_data->month, $arch_data->day + 1, $arch_data->year) - $user->timezone;

  $arch_data->start_of_month = mktime(0, 0, 0, $arch_data->month, 1, $arch_data->year);

  // Extract first day of the month:
  $arch_data->month_first = date('w', $arch_data->start_of_month);

  // Extract last day of the month:
  $arch_data->month_last = date('t', $arch_data->start_of_month);

  $arch_data->end_of_month = mktime(23, 59, 59, $arch_data->month, $arch_data->month_last, $arch_data->year);
  /*
  ** Prepare the values of the form fields:
  */
  $arch_data->years  = archive_years();
  $arch_data->months = archive_months($arch_data->year);
  $arch_data->days   = archive_days($arch_data);

  return $arch_data;
}

function archive_page() {
  $arch_data = _archive_selected_date();

  $output = theme("archive_navigation", $arch_data);

  /*
  ** Fetch nodes for the selected date, or current date if none
  ** selected.
  */
  $result = pager_query(archive_buildQuery($arch_data), variable_get('default_nodes_main', 10));

  if (db_num_rows($result)) {
    while ($node = db_fetch_object($result)) {
      $output .= node_view(node_load(array('nid' => $node->nid, 'type' => $node->type)), 1);
    }
    $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
  }

  print theme("page", $output);

}

/**************************************
* Helper Functions
***************************************/

function archive_buildQuery(&$ad) {
  global $user;

  if ($ad->day > 0 && $ad->month > 0) {
    // Confine the display interval to only one day.
    $date     = mktime(0, 0, 0, $ad->month, $ad->day, $ad->year) - $user->timezone;
    $date_end = mktime(0, 0, 0, $ad->month, $ad->day + 1, $ad->year) - $user->timezone;
  }
  elseif ($ad->month > 0) {
    // Confine the display interval to one month.
    $date     = mktime(0, 0, 0, $ad->month, 1, $ad->year) - $user->timezone;
    $date_end = mktime(0, 0, 0, $ad->month+1, 0, $ad->year) - $user->timezone;
  }
  else {
    // Confine the display interval to one year.
    $date     = mktime(0, 0, 0, 1, 1, $ad->year) - $user->timezone;
    $date_end = mktime(0, 0, 0, 1, 0, $ad->year+1) - $user->timezone;
  }

  // wheeeee!
  return "SELECT nid, type FROM {node} WHERE status = '1' AND created < $date_end AND created >= $date ORDER BY created DESC";
}

/*
* Returns the range of years with posts.
*/
function archive_years() {
  $result = db_query("SELECT MIN(created) AS min_date, MAX(created) AS max_date FROM {node} WHERE status = 1");
  $min_max = db_fetch_object($result);
  return array(date("Y",$min_max->min_date),date("Y",$min_max->max_date));
}

/*
* Returns the months of a given year which have nodes.
*/
function archive_months($year) {
  global $user;

  $begin  = mktime(0,0,0,1,1,$year) - $user->timezone;;
  $end    = mktime(0,0,0,1,1,$year+1) - $user->timezone;;

  $result = db_query("SELECT DISTINCT MONTH(FROM_UNIXTIME(created)) AS m_month FROM {node} WHERE  STATUS = 1 AND created > $begin AND created < $end");
  $months_with_posts = array();
  while ($month = db_fetch_object($result)) {
    $months_with_posts[] = $month->m_month;
  }

  return $months_with_posts;
}

/*
* Returns the days of a given month which have nodes.
*/
function archive_days(&$ad) {
  global $user;

  $sql = 'SELECT n.nid, n.created FROM {node} n WHERE n.status = 1 AND n.created > %d AND n.created < %d ORDER BY n.created';
  $sql = db_rewrite_sql($sql);
  
  $result = db_query($sql, $ad->start_of_month, $ad->end_of_month);

  $days_with_posts = array();
  while ($day_with_post = db_fetch_object($result)) {
    $daynum = date('j', $day_with_post->created + $user->timezone);
    if (isset($days_with_posts[$daynum])) {
      $days_with_posts[$daynum]++;
    }
    else {
      $days_with_posts[$daynum] = 1;
    }
  }

  return $days_with_posts;
}

function archive_validYear($year) {
  return (1990 < $year && $year < 2040);
}

function archive_validMonth($month) {
  return (1 <= $month && $month <= 12);
}

function archive_validDay($day, $month, $year) {
  if (archive_validMonth($month) && archive_validYear($year)) {
    // Extract last day of the month:
    $last = date("t", mktime(0,0,0,$month,1,$year));

    return (1 <= $day && $day <= $last);
  }
  else {
    return false;
  }
}

function archive_buildURL($cur_state, $y = 0, $m = 0, $d = 0) {
  $url = 'archive';

  if (archive_validYear($y)) {
    $url .= '/'. $y;

    if (archive_validMonth($m)) {
      $url .= '/'. $m;

      if (archive_validDay($d, $m, $y)) {
        $url .= '/'. $d;
      }
    }
  }
  elseif (archive_validYear($cur_state->year) && archive_validMonth($m)) {
    $url .= '/'. $cur_state->year .'/'. $m;

    if (archive_validDay($d, $m, $cur_state->year)) {
      $url .= '/'. $d;
    }
  }
  elseif (archive_validYear($cur_state->year) && archive_validMonth($cur_state->month)) {
    $url .= '/'. $cur_state->year .'/'. $cur_state->month;

    if (archive_validDay($d, $cur_state->month, $cur_state->year)) {
      $url .= '/'. $d;
    }
  }
  return $url;
}

/**************************************
* Theme Functions
***************************************/
function theme_archive_navigation($arch_data) {
  $output  = "\n  <div class=\"box\" id=\"archive_container\">\n";

  $output .= "    <dl>\n";
  $output .= "      <dt>". t('Date') ."</dt>\n";
  $output .= "      <dd>\n";
  $output .= theme("archive_navigation_years", $arch_data);

  if (archive_validYear($arch_data->year)) {
    $output .= theme("archive_navigation_months", $arch_data);
  }
  if (archive_validMonth($arch_data->month)) {
    $output .= theme("archive_navigation_days", $arch_data);
  }
  $output .= "      </dd>\n";
  $output .= "    </dl>\n";
  $output .= "  </div>\n";
  return $output;
}

function theme_archive_navigation_years($arch_data) {
  $output = "      <ul id=\"arch_years\">\n";
  for ($year=min($arch_data->years); $year <= max($arch_data->years); $year++) {
    $class = "";

    if ($year > date("Y",$arch_data->today)) {
      $class = " class=\"future\"";
    }
    elseif ($year == $arch_data->year) {
      $class = " class=\"selected\"";
    }

    if (min($arch_data->years) <= $year && $year <= max($arch_data->years) ) {
        $output .= "        <li$class>". l(date("Y",mktime(0,0,0,1,1,$year)), archive_buildURL($arch_data, $year)) ."</li>\n";
    }
  }
  $output .= "      </ul>\n";

  return $output;
}

function theme_archive_navigation_months($arch_data) {
  $output = "";

  $output = "      <ul id=\"arch_months\">\n";
  for ($month=1; $month <= 12; $month++) {
    $class = "";

    if ($month == $arch_data->month) {
      $class = " class=\"selected\"";
    }

    if (in_array($month, $arch_data->months)) {
        $output .= "        <li$class>". l(date("M",mktime(0,0,0,$month,1,$arch_data->year)), archive_buildURL($arch_data, 0, $month)) ."</li>\n";
    }
    else {
      $output   .= "        <li$class>". date("M",mktime(0,0,0,$month,1,$arch_data->year)) ."</li>\n";
    }
  }
  $output .= "      </ul>\n";

  return $output;
}

function theme_archive_navigation_days($arch_data) {
  $output = "";

  $day_stop  = date("t", mktime(0,0,0,$arch_data->month,1,$arch_data->year));

  $output = "      <ul id=\"arch_days\">\n";

  for ($day = 1; $day <= $day_stop; $day++) {
    $class = "";

    if (isset($arch_data->days[$day]) && $arch_data->days[$day] > 0) {
      if ($day == $arch_data->day) {
        $class = " class=\"selected\"";
      }
      $output .= "        <li$class>". l(date("d",mktime(0,0,0,$arch_data->month,$day,$date->year)), archive_buildURL($arch_data, 0, 0, $day), array('title' => $arch_data->days[$day] .' posts')) ."</li>\n";
    }
    else {
      $output .= "        <li$class>". date("d",mktime(0,0,0,$arch_data->month,$day,$arch_data->year)) ."</li>\n";
    }
  }

  $output .= "      </ul>\n";

  return $output;
}
?>