Location: PHPKode > scripts > Calendar Solution > convissor-calendar_solution-ff69243/include/CalendarSolution/List.php
<?php

/**
 * Calendar Solution's parent class for displaying collections of events
 *
 * @package CalendarSolution
 * @author Daniel Convissor <hide@address.com>
 * @copyright The Analysis and Solutions Company, 2002-2012
 * @license http://www.analysisandsolutions.com/software/license.htm Simple Public License
 */

/**
 * The parent class for displaying collections of events
 *
 * @package CalendarSolution
 * @author Daniel Convissor <hide@address.com>
 * @copyright The Analysis and Solutions Company, 2002-2012
 * @license http://www.analysisandsolutions.com/software/license.htm Simple Public License
 */
abstract class CalendarSolution_List extends CalendarSolution {
	/**
	 * The cache key identifying the number of rows found in a data set
	 * @var string
	 */
	protected $cache_count_key;

	/**
	 * The data cache key for the current view
	 * @var string
	 */
	protected $cache_key;

	/**
	 * Has set_prior_and_next_dates() been called?
	 * @var bool
	 */
	protected $called_set_prior_and_next_dates = false;

	/**
	 * Has set_request_properties() been called?
	 * @var bool
	 */
	protected $called_set_request_properties = false;

	/**
	 * The category ids to show events for
	 * @var array
	 */
	protected $category_id;

	/**
	 * The category ids to not show events for
	 * @var array
	 */
	protected $category_id_not;

	/**
	 * The records to be displayed
	 * @var array
	 */
	protected $data;

	/**
	 * The frequent event id to show events for
	 * @var int
	 */
	protected $frequent_event_id;

	/**
	 * The frequent event id to not show events for
	 * @var array
	 */
	protected $frequent_event_id_not;

	/**
	 * The start of the date range to show events for in the current request
	 * @var DateTimeSolution
	 */
	protected $from;

	/**
	 * The DateIntervalSolution specification for how many months to show at once
	 * @var string
	 */
	protected $interval_spec;

	/**
	 * Only show events sponsored by you ("Y") or only those sponsored by
	 * another organization ("N")?
	 *
	 * All events are shown if this is empty.
	 *
	 * @var string  ("Y" or "N")
	 */
	protected $is_own_event;

	/**
	 * The number of items to show
	 * @var int
	 */
	protected $limit_quantity;

	/**
	 * The zero-based row index to start the limit on
	 * @var int
	 */
	protected $limit_start;

	/**
	 * The start of the date range to show events for if the user navigates
	 * to later events
	 * @var DateTimeSolution
	 */
	protected $next_from;

	/**
	 * The end of the date range to show events for if the user navigates
	 * to later events
	 * @var DateTimeSolution
	 */
	protected $next_to;

	/**
	 * The page id to show events for
	 * @var int
	 */
	protected $page_id;

	/**
	 * The furthest date in the future users are allowed to see
	 * @var DateTimeSolution
	 */
	protected $permit_future_date;

	/**
	 * The furthest date in the past users are allowed to see
	 * @var DateTimeSolution
	 */
	protected $permit_history_date;

	/**
	 * The start of the date range to show events for if the user navigates
	 * to earlier events
	 * @var DateTimeSolution
	 */
	protected $prior_from;

	/**
	 * The end of the date range to show events for if the user navigates
	 * to earlier events
	 * @var DateTimeSolution
	 */
	protected $prior_to;

	/**
	 * Should cancelled events be shown or not?
	 * @var bool
	 */
	protected $show_cancelled = true;

	/**
	 * Should the Location field be shown or not when viewing "Calendar" mode?
	 *
	 * NOTE: needs to be in CalendarSolution_List class due to ability switch
	 * between Calendar and List views.
	 *
	 * @var bool
	 */
	protected $show_location = true;

	/**
	 * Show your own events before events produced by other organizations?
	 * @var bool
	 */
	protected $show_own_events_first = false;

	/**
	 * Should the Summary field be shown or not when viewing "List" mode?
	 *
	 * NOTE: needs to be in CalendarSolution_List class due to ability switch
	 * between Calendar and List views.
	 *
	 * @var bool
	 */
	protected $show_summary = true;

	/**
	 * Format for PHP's date() function, to be used by our format_date() method
	 *
	 * @see CalendarSolution_List::set_time_format()
	 * @see CalendarSolution::format_date()
	 * @var string
	 */
	protected $time_format = self::DATE_FORMAT_TIME_12AP;

	/**
	 * The end of the date range to show events for in the current request
	 * @var DateTimeSolution
	 */
	protected $to;

	/**
	 * How many rows the entire result set has
	 * @var int
	 */
	protected $total_rows;

	/**
	 * The SQL WHERE clause for the current view
	 * @var string
	 */
	protected $where_sql;


	/**
	 * Calls the parent constructor and set_uri() then populates the
	 * "$interval_spec" property
	 *
	 * @param integer $months  how many months should be shown at once
	 * @param string $dbms  optional override of the database extension setting
	 *                      in CALENDAR_SOLUTION_DBMS.  Values can be
	 *                      "mysql", "mysqli", "pgsql", "sqlite", "sqlite3".
	 *
	 * @uses CalendarSolution::__construct()  to instantiate the database and
	 *       cache classes
	 * @uses CalendarSolution_List::$interval_spec  is set based on the
	 *       "$months" parameter; is used later when creating new
	 *       DateIntervalSolution objects
	 * @uses CalendarSolution::set_uri()  to set the "$uri" property
	 */
	public function __construct($months = 3, $dbms = CALENDAR_SOLUTION_DBMS) {
		parent::__construct($dbms);
		$this->interval_spec = 'P' . ($months - 1) . 'M';
		$this->set_uri();
	}

	/**
	 * Looks at $_REQUEST['view'] then $_COOKIE['CalendarSolution']
	 * to determine whether to return a CalendarSolution_List_Calendar
	 * (the default) or CalendarSolution_List_List object
	 *
	 * WARNING: this function sets a cookie, so MUST be called before ANY
	 * output is sent to the browser.
	 *
	 * @param integer $months  how many months should be shown at once
	 * @param string $dbms  optional override of the database extension setting
	 *                      in CALENDAR_SOLUTION_DBMS.  Values can be
	 *                      "mysql", "mysqli", "pgsql", "sqlite", "sqlite3".
	 *
	 * @return CalendarSolution_List_Calendar|CalendarSolution_List_List
	 *
	 * @todo Adjust cookie expiration date as the year 2038 approaches.
	 */
	public static function factory_chosen_view($months = 3,
		$dbms = CALENDAR_SOLUTION_DBMS)
	{
		$set_cookie = false;

		if (empty($_REQUEST['view'])) {
			if (empty($_COOKIE['CalendarSolution'])) {
				$view = 'Calendar';
				$set_cookie = true;
			} elseif ($_COOKIE['CalendarSolution'] == 'List') {
				$view = 'List';
			} else {
				$view = 'Calendar';
			}
		} elseif ($_REQUEST['view'] == 'List') {
			$view = 'List';
			if (empty($_COOKIE['CalendarSolution'])
				|| $_COOKIE['CalendarSolution'] == 'Calendar')
			{
				$set_cookie = true;
			}
		} else {
			$view = 'Calendar';
			if (empty($_COOKIE['CalendarSolution'])
				|| $_COOKIE['CalendarSolution'] == 'List')
			{
				$set_cookie = true;
			}
		}

		if ($set_cookie) {
			// Expires in 2038.
			setcookie('CalendarSolution', $view, 2147483647, '/');
		}

		$class = __CLASS__ . '_' . $view;
		return new $class($months, $dbms);
	}

	/**
	 * Produces the HTML for changing between "List" and "Calendar" formats
	 *
	 * @param string $phrase  the sprintf formatted string to be displayed.
	 *                        Must contain one "%s" conversion specification,
	 *                        where the "$list" or "$calendar" parameter text
	 *                        will be inserted by this method as appropriate
	 * @param string $list  the text for the "list" view.  The value is passed
	 *                      through htmlspecialchars().
	 * @param string $calendar  the text for the "calendar" view.  The value is
	 *                          passed through htmlspecialchars().
	 *
	 * @return string  the HTML for switching views
	 *
	 * @uses CalendarSolution_List::set_request_properties()  to determine the
	 *       user's intent
	 * @uses CalendarSolution_List::$view  to know the current view
	 * @uses CalendarSolution_List::$category_id  to know the current category
	 * @uses CalendarSolution_List::$category_id_not  to know the current
	 *       categories to not show
	 * @uses CalendarSolution_List::$frequent_event_id  to know the current
	 *       event
	 * @uses CalendarSolution_List::$frequent_event_id_not  to know the current
	 *       event to not show
	 * @uses CalendarSolution_List::$from  to know the current from date
	 * @uses CalendarSolution_List::$to  to know the current to date
	 * @uses CalendarSolution::$uri  to know the current URI
	 *
	 * @throws CalendarSolution_Exception if not in Calendar or List view
	 *
	 * @since Method available since version 3.0
	 */
	public function get_change_view($phrase = 'View the events in %s format',
			$list = 'list', $calendar = 'calendar')
	{
		if (!$this->called_set_request_properties) {
			$this->set_request_properties();
		}

		$uri = $this->uri;

		$uri['query']['category_id'] = empty($this->category_id)
				? null : $this->category_id;
		$uri['query']['category_id_not'] = empty($this->category_id_not)
				? null : $this->category_id_not;
		$uri['query']['frequent_event_id'] = empty($this->frequent_event_id)
				? null : $this->frequent_event_id;
		$uri['query']['frequent_event_id_not'] = empty($this->frequent_event_id_not)
				? null : $this->frequent_event_id_not;
		$uri['query']['from'] = empty($this->from)
				? null : $this->from->format('Y-m-d');
		$uri['query']['to'] = empty($this->to)
				? null : $this->to->format('Y-m-d');

		if ($this->view == 'Calendar') {
			$uri['query']['view'] = 'List';
			$anchor = htmlspecialchars($list);
		} elseif ($this->view == 'List') {
			$uri['query']['view'] = 'Calendar';
			$anchor = htmlspecialchars($calendar);
		} else {
			throw new CalendarSolution_Exception('get_change_view() for use'
					. ' only with Calendar or List views');
		}

		$link = '<a href="'
			. htmlspecialchars($uri['path'] . '?' . http_build_query($uri['query']))
			. '">' . $anchor . '</a>';

		$format = '<div class="cs_change_view">' . $phrase . "</div>\n";
		return sprintf($format, $link);
	}

	/**
	 * Provides the path and name of the needed Cascading Style Sheet file
	 *
	 * @return string  the path and name of the CSS file
	 */
	public function get_css_name() {
		return dirname(__FILE__) . '/List.css';
	}

	/**
	 * Produces the HTML for navigating to earlier and later events
	 *
	 * Inteneded for use with the "Calendar," "List," and "MonthTitle" formats.
	 *
	 * NOTE: will return an empty string if "$from" or "$to" are false.
	 *
	 * @param string $prior_link  the text for the "prior" link.  The value is
	 *                      passed through htmlspecialchars().
	 * @param string $next_link  the text for the "next" link.  The value is
	 *                      passed through htmlspecialchars().
	 *
	 * @return string  the navigation section's HTML
	 *
	 * @uses CalendarSolution_List::set_request_properties()  to determine the
	 *       user's intent
	 * @uses CalendarSolution_List::set_prior_and_next_dates()  to determine
	 *       dates for the prior and next links
	 * @uses CalendarSolution_List::set_permit_history_months()  to limit how
	 *       far back people can go
	 * @uses CalendarSolution_List::set_permit_future_months()  to limit how
	 *       far in the future people can go
	 * @uses CalendarSolution_List::$view  to know the current view
	 * @uses CalendarSolution_List::$category_id  to know the current category
	 * @uses CalendarSolution_List::$category_id_not  to know the current
	 *       categories to not show
	 * @uses CalendarSolution_List::$frequent_event_id  to know the current
	 *       event
	 * @uses CalendarSolution_List::$frequent_event_id_not  to know the current
	 *       events to not show
	 * @uses CalendarSolution_List::$from  to know the current from date
	 * @uses CalendarSolution_List::$to  to know the current to date
	 * @uses CalendarSolution_List::$prior_from  for the "prior" link's from date
	 * @uses CalendarSolution_List::$prior_to  for the "prior" link's to date
	 * @uses CalendarSolution_List::$next_from  for the "next" link's from date
	 * @uses CalendarSolution_List::$next_to  for the "next" link's to date
	 * @uses CalendarSolution::$uri  to know the current URI
	 *
	 * @since Method available since version 3.0
	 */
	public function get_date_navigation($prior_link = '< See Earlier Events',
			$next_link = 'See Later Events >')
	{
		if (!$this->called_set_request_properties) {
			$this->set_request_properties();
		}
		if ($this->from === false || $this->to === false) {
			return '';
		}
		if (!$this->called_set_prior_and_next_dates) {
			$this->set_prior_and_next_dates();
		}
		if ($this->permit_history_date === null) {
			$this->set_permit_history_months();
		}
		if ($this->permit_future_date === null) {
			$this->set_permit_future_months();
		}

		$uri = $this->uri;

		$uri['query']['category_id'] = empty($this->category_id)
				? null : $this->category_id;
		$uri['query']['category_id_not'] = empty($this->category_id_not)
				? null : $this->category_id_not;
		$uri['query']['frequent_event_id'] = empty($this->frequent_event_id)
				? null : $this->frequent_event_id;
		$uri['query']['frequent_event_id_not'] = empty($this->frequent_event_id_not)
				? null : $this->frequent_event_id_not;
		$uri['query']['view'] = $this->view;

		$uri['query']['from'] = $this->prior_from->format('Y-m-d');
		$uri['query']['to'] = $this->prior_to->format('Y-m-d');

		$out = '<div class="cs_date_navigation">';
		$out .= '<div class="cs_prior">';
		if ($this->prior_to > $this->permit_history_date) {
			$out .= '<a href="'
				. htmlspecialchars($uri['path'] . '?' . http_build_query($uri['query']))
				. '">' . htmlspecialchars($prior_link) . '</a>';
		}
		$out .= '</div>';

		$uri['query']['from'] = $this->next_from->format('Y-m-d');
		$uri['query']['to'] = $this->next_to->format('Y-m-d');

		$out .= '<div class="cs_next">';
		if ($this->next_from < $this->permit_future_date
			|| $this->permit_future_date === false)
		{
			$out .= '<a href="'
				. htmlspecialchars($uri['path'] . '?' . http_build_query($uri['query']))
				. '">' . htmlspecialchars($next_link) . '</a>';
		}
		$out .= '</div>';

		$out .= "</div>\n";

		return $out;
	}

	/**
	 * Produces the HTML for the form people can use to pick date ranges
	 * and particular events
	 *
	 * NOTE: "datelist" will not be displayed if "$from", "$to",
	 * "$permit_history_date", or "$permit_future_date" are false.
	 *
	 * @param array $show  a list of elements to show ("datebox", "datelist",
	 *                     "category", "event", "remove")
	 *
	 * @return string  the HTML for the limit form
	 *
	 * @uses CalendarSolution_List::$category_id  to set the default category
	 * @uses CalendarSolution_List::$frequent_event_id  to set the default event
	 * @uses CalendarSolution_List::$from  to set the default "datebox" date
	 * @uses CalendarSolution_List::$to  to set the default "datebox" date
	 * @uses CalendarSolution_List::$permit_history_date  to determine the
	 *       earliest value in the "datelist" dropdown boxes
	 * @uses CalendarSolution_List::$permit_future_date  to determine the
	 *       latest value in the "datelist" dropdown boxes
	 * @uses CalendarSolution::$uri  to know the current URI
	 * @uses CalendarSolution_List::set_request_properties()  to determine the
	 *       users intentions
	 *
	 * @since Method available since version 3.0
	 */
	public function get_limit_form(
			$show = array('datebox', 'category', 'event', 'remove'))
	{
		if (!$this->called_set_request_properties) {
			$this->set_request_properties();
		}

		$uri = $this->uri;

		$uri['query']['limit'] = null;
		$uri['query']['from'] = null;
		$uri['query']['to'] = null;
		$uri['query']['category_id'] = null;
		$uri['query']['frequent_event_id'] = null;

		$action = htmlspecialchars($uri['path'] . '?' . http_build_query($uri['query']));

		$out = '<form class="cs_limit" method="get" action="' . $action . '">';

		if (in_array('datebox', $show)) {
			if ($this->from) {
				$from = $this->from->format('Y-m-d');
			} else {
				$from = '';
			}

			if ($this->to) {
				$to = $this->to->format('Y-m-d');
			} else {
				$to = '';
			}

			$out .= '<div class="cs_date_limit_box">'
				. '<label for="from">Limit to dates between </label>'
				. '<input id="from" type="text" size="11" maxlength="10"'
				. ' name="from" value="' . $from . '" />'
				. '<label for="to"> and </label>'
				. '<input id="to" type="text" size="11" maxlength="10" '
				. 'name="to" value="' . $to . '" /></div>' . "\n";
		}

		if (in_array('datelist', $show)
			&& $this->from !== false
			&& $this->to !== false
			&& $this->permit_history_date !== false
			&& $this->permit_future_date !== false)
		{
			if ($this->permit_history_date === null) {
				$this->set_permit_history_months();
			}
			$from = new DateTimeSolution($this->permit_history_date->format('Y-m-d'));

			if ($this->permit_future_date === null) {
				$this->set_permit_future_months();
			}

			$one_month = new DateIntervalSolution('P1M');
			$from_list = '';
			$to_list = '';
			$from_view = $this->from->format('Y-m-d');
			$to_view = $this->to->format('Y-m-d');

			while ($from < $this->permit_future_date) {
				$from_i = $from->format('Y-m-d');
				$to_i = $from->format('Y-m-t');

				if ($from_view == $from_i) {
					$from_selected = '" selected="selected';
				} else {
					$from_selected = '';
				}

				if ($to_view == $to_i) {
					$to_selected = '" selected="selected';
				} else {
					$to_selected = '';
				}

				$from_list .= '<option value="' . $from_i . $from_selected
					. '">' . $from_i . "</option>\n";
				$to_list .= '<option value="' . $to_i . $to_selected
					. '">' . $to_i . "</option>\n";

				$from->add($one_month);
			}

			$out .= '<div class="cs_date_limit_list">'
				. '<label for="from">Limit to dates between </label>'
				. '<select id="from" size="0" name="from">'
				. "\n" . $from_list . '</select>';

			$out .= '<label for="to"> and </label>'
				. '<select id="to" size="0" name="to">'
				. "\n" . $to_list . '</select>';

			$out .= "</div>\n";
		}

		if (in_array('category', $show)) {
			$out .= '<div class="cs_category_limit"><label for="category_id">'
				. 'Categories: </label>';

			if ($this->use_cache) {
				$cache_key = 'category_list:' . $this->category_id;
				$list = $this->cache->get($cache_key);
				$memcache_result = ($list !== false);
			} else {
				$memcache_result = null;
			}
			if (!$memcache_result) {
				$opt = array(
					'id'           => 'category_id',
					'table'        => 'cs_category',
					'visiblefield' => 'category',
					'keyfield'     => 'category_id',
					'name'         => 'category_id',
					'orderby'      => 'category',
					'where'        => '1 = 1',
					'multiple'     => 'N',
					'size'         => '0',
					'default'      => $this->category_id,
					'add'          => array('' => 'Pick a Category, if you want to')
				);

				$list = $this->sql->GetOptionListGenerator(__FILE__, __LINE__, $opt);

				if ($this->use_cache) {
					$this->cache->set($cache_key, $list);
				}
			}
			$out .= "$list</div>\n";
		}

		if (in_array('event', $show)) {
			$out .= '<div class="cs_frequent_event_limit">'
				. '<label for="frequent_event_id">'
				. 'Frequent Events: </label>';

			if ($this->use_cache) {
				$cache_key = 'frequent_event_list:' . $this->frequent_event_id;
				$list = $this->cache->get($cache_key);
				$memcache_result = ($list !== false);
			} else {
				$memcache_result = null;
			}
			if (!$memcache_result) {
				$opt = array(
					'id'           => 'frequent_event_id',
					'table'        => 'cs_frequent_event',
					'visiblefield' => 'frequent_event',
					'keyfield'     => 'frequent_event_id',
					'name'         => 'frequent_event_id',
					'orderby'      => 'frequent_event',
					'where'        => '1 = 1',
					'multiple'     => 'N',
					'size'         => '0',
					'default'      => $this->frequent_event_id,
					'add'          => array('' => 'Pick a Frequent Event, if you want to')
				);

				$list = $this->sql->GetOptionListGenerator(__FILE__, __LINE__, $opt);

				if ($this->use_cache) {
					$this->cache->set($cache_key, $list);
				}
			}
			$out .= "$list</div>\n";
		}

		$out .= '<div class="cs_submit_limit">'
			. '<input type="submit" name="limit" value="Limit" />';

		if (in_array('remove', $show)) {
			$out .= '<input type="submit" name="remove_limit" value="Remove Limits" />';
		}

		$out .= "</div></form>\n";

		return $out;
	}

	/**
	 * Produces the prior/next links for use in conjunction with set_limit()
	 *
	 * For this navigation to work properly, set_limit()'s "$start" parameter
	 * must be set to NULL.  For example: <kbd>set_limit(10, null)</kbd>.
	 *
	 * NOTE: This must be called after get_rendering().  An empty string will
	 * be returned if called before get_rendering().
	 *
	 * @param string $prior_link  the text for the "prior" link.  The value is
	 *                            passed through htmlspecialchars().
	 * @param string $next_link  the text for the "next" link.  The value is
	 *                           passed through htmlspecialchars().
	 *
	 * @return string  the navigation elements if the $start parameter is
	 *                 enabled in set_limit() and get_rendering() has been
	 *                 called, an empty string if not
	 *
	 * @see CalendarSolution_List::set_limit()
	 * @uses CalendarSolution::$uri  to know the current URI
	 *
	 * @since Method available since version 3.0
	 */
	public function get_limit_navigation($prior_link = '< prior',
			$next_link = 'next >')
	{
		if (!is_numeric($this->limit_start) || !$this->total_rows) {
			return '';
		}

		$uri = $this->uri;

		$out = '<div class="cs_limit_navigation"><div class="cs_prior">';
		$prior_start = $this->limit_start - $this->limit_quantity;
		if ($prior_start > -$this->limit_quantity) {
			if ($prior_start < 0) {
				$prior_start = 0;
			}
			$uri['query']['limit_start'] = $prior_start;
			$out .= '<a href="'
				. htmlspecialchars($uri['path'] . '?' . http_build_query($uri['query']))
				. '">' . htmlspecialchars($prior_link) . '</a>';
		}
		$out .= '</div>';

		$out .= '<div class="cs_next">';
		$next_start = $this->limit_start + $this->limit_quantity;
		if ($next_start < $this->total_rows) {
			$uri['query']['limit_start'] = $next_start;
			$out .= '<a href="'
				. htmlspecialchars($uri['path'] . '?' . http_build_query($uri['query']))
				. '">' . htmlspecialchars($next_link) . '</a>';
		}
		$out .= "</div></div>\n";

		return $out;
	}

	/**
	 * Figures out what link should be shown for this item
	 *
	 * @param array $event  the associative array of the current item
	 *
	 * @return string  the hyperlink and/or title
	 *
	 * @uses CALENDAR_SOLUTION_LINK_PATH  for pointing links to the
	 *       calendar-detail.php page in the right direction
	 */
	public function get_link($event) {
		if ($this->is_admin()) {
			$uri = 'calendar-detail.php?calendar_id=' . $event['calendar_id'];
		} else {
			switch ($event['list_link_goes_to_id']) {
				case self::LINK_TO_DETAIL_PAGE:
					$uri = CALENDAR_SOLUTION_LINK_PATH
						. 'calendar-detail.php?calendar_id='
						. $event['calendar_id'];
					break;
				case self::LINK_TO_FREQUENT_EVENT_URI:
					$uri = $event['frequent_event_uri'];
					break;
				case self::LINK_TO_CALENDAR_URI:
					$uri = $event['calendar_uri'];
					break;
				default:
					return $event['title'];
			}
		}

		return '<a href="' . $uri . '">' . $event['title'] . '</a>';
	}

	/**
	 * Says which view class is being used
	 *
	 * @return string  the view being used
	 *
	 * @since Method available since version 3.0
	 */
	public function get_view() {
		return $this->view;
	}

	/**
	 * Creates a DateIntervalSolution object indicating how many months should be
	 * displayed at one time
	 *
	 * @return DateIntervalSolution
	 *
	 * @uses CalendarSolution_List::$interval_spec  to know how long the
	 *       interval should be
	 */
	protected function interval_singleton() {
		static $out;
		if (!isset($out)) {
			$out = new DateIntervalSolution($this->interval_spec);
		}
		return $out;
	}

	/**
	 * Assembles the query string then executes it
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::set_where_sql()  to generate the SQL's
	 *       WHERE clause and needed cache keys
	 * @uses CalendarSolution_List::$limit_quantity  to limit the number of
	 *       rows shown
	 * @uses CalendarSolution_List::$limit_start  to determine where to start
	 *       the number of rows to be shown
	 * @uses CalendarSolution::$cache  to cache the results, if possible
	 * @uses CalendarSolution_List::$data  to hold the results
	 * @uses CalendarSolution_List::$where_sql  as the query's WHERE clause
	 * @uses CalendarSolution_List::$cache_key  to know where the data is stored
	 * @uses CalendarSolution_List::$cache_count_key  to know where the row
	 *       count is stored
	 *
	 * @throws CalendarSolution_Exception if to is later than from
	 */
	protected function run_query() {
		if (!$this->where_sql) {
			$this->set_where_sql();
		}

		$start_set = is_numeric($this->limit_start);

		/*
		 * Get data from cache, if available.
		 */

		if ($this->use_cache) {
			$this->data = $this->cache->get($this->cache_key);
			if ($this->data !== false) {
				if ($start_set) {
					$this->total_rows = $this->cache->get($this->cache_count_key);
				}
				return;
			}
		}

		/*
		 * Compose LIMIT and run COUNT if needed.
		 */

		$limit_sql = '';
		if ($this->limit_quantity) {
			$limit_sql = "
				LIMIT " . $this->limit_quantity
				. " OFFSET " . (int) $this->limit_start;

			if ($start_set) {
				$this->sql->SQLQueryString = "SELECT COUNT(*) AS ct
					FROM cs_calendar
					$this->where_sql";

				$this->sql->RunQuery(__FILE__, __LINE__);
				$event = $this->sql->RecordAsEnumArray(__FILE__, __LINE__);
				$this->total_rows = $event[0];
			}
		}

		if ($this->show_own_events_first) {
			$is_own_event = 'is_own_event DESC,';
		} else {
			$is_own_event = '';
		}

		/*
		 * Construct the SQL string.
		 */

		$this->sql->SQLQueryString = "SELECT
			calendar_id,
			calendar_uri,
			changed,
			date_start,
			frequent_event_uri,
			is_own_event,
			list_link_goes_to_id,
			location_start,
			note,
			status,
			cs_calendar.status_id AS status_id,
			summary,
			time_end,
			time_start,
			title
			FROM cs_calendar
			LEFT JOIN cs_frequent_event USING (frequent_event_id)
			LEFT JOIN cs_status
				ON (cs_status.status_id = cs_calendar.status_id)";

		$this->sql->SQLQueryString .= $this->where_sql;

		$this->sql->SQLQueryString .= "
			ORDER BY date_start, $is_own_event time_start, title,
				cs_calendar.frequent_event_id";

		$this->sql->SQLQueryString .= $limit_sql;

		$this->sql->RunQuery(__FILE__, __LINE__);

		/*
		 * Process database results.
		 */

		$this->data = array();
		$skip_markup = array(
			'calendar_id',
			'calendar_uri',
			'changed',
			'date_start',
			'frequent_event_uri',
			'list_link_goes_to_id',
			'status_id',
			'time_end',
			'time_start',
			'title',
		);
		while ($event = $this->sql->RecordAsAssocArray(
				__FILE__, __LINE__, $skip_markup))
		{
			$this->data[] = $event;
		}
		$this->sql->ReleaseRecordSet(__FILE__, __LINE__);

		if ($this->use_cache) {
			$this->cache->set($this->cache_key, $this->data);
			if ($start_set) {
				$this->cache->set($this->cache_count_key, $this->total_rows);
			}
		}
	}

	/**
	 * Sets the "category_id" property to the appropriate value
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['category_id']
	 *                   though set it to FALSE if it is not set or invalid
	 *                   + FALSE = set the value to FALSE
	 *                   + integer = an integer, falling back to FALSE if
	 *                   the input is is invalid
	 *                   + array of integers = if each element is not an integer
	 *                   then the value will be set to FALSE
	 * @return void
	 *
	 * @uses CalendarSolution::get_int_array_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$category_id  to store the data
	 *
	 * @since Method available since version 3.0
	 */
	public function set_category_id($in = null) {
		if ($in === null) {
			$out = $this->get_int_array_from_request('category_id');
		} else {
			if (!is_array($in)) {
				$in = array($in);
			}
			$out = array();
			foreach ($in as $value) {
				if (!is_scalar($value)
					|| !preg_match('/^\d{1,10}$/', $value))
				{
					$this->category_id = false;
					return;
				}
				$out[] = $value;
			}
		}

		$this->category_id = $out;
	}

	/**
	 * Sets the "category_id_not" property to the appropriate value
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['category_id_not']
	 *                   though set it to FALSE if it is not set or invalid
	 *                   + FALSE = set the value to FALSE
	 *                   + integer = an integer, falling back to FALSE if
	 *                   the input is is invalid
	 *                   + array of integers = if each element is not an integer
	 *                   then the value will be set to FALSE
	 * @return void
	 *
	 * @uses CalendarSolution::get_int_array_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$category_id_not  to store the data
	 *
	 * @since Method available since version 3.7
	 */
	public function set_category_id_not($in = null) {
		if ($in === null) {
			$out = $this->get_int_array_from_request('category_id_not');
		} else {
			if (!is_array($in)) {
				$in = array($in);
			}
			$out = array();
			foreach ($in as $value) {
				if (!is_scalar($value)
					|| !preg_match('/^\d{1,10}$/', $value))
				{
					$this->category_id = false;
					return;
				}
				$out[] = $value;
			}
		}

		$this->category_id_not = $out;
	}

	/**
	 * Sets the date format to be used by our format_date() method
	 *
	 * @param int $in  the format used by PHP's date() function
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::$date_format  to store the data
	 * @see CalendarSolution::format_date()
	 *
	 * @since Method available since version 3.0
	 */
	public function set_date_format($in) {
		$this->date_format = $in;
	}

	/**
	 * Sets the "frequent_event_id" property to the appropriate value
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['frequent_event_id']
	 *                   though set it to FALSE if it is not set or invalid
	 *                   + FALSE = set the value to FALSE
	 *                   + integer = an integer, falling back to FALSE if
	 *                   the input is is invalid
	 * @return void
	 *
	 * @uses CalendarSolution::get_int_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$frequent_event_id  to store the data
	 */
	public function set_frequent_event_id($in = null) {
		if ($in === null) {
			$in = $this->get_int_from_request('frequent_event_id');
		}
		if (!is_scalar($in) || !preg_match('/^\d{1,10}$/', $in)) {
			$in = false;
		}

		$this->frequent_event_id = $in;
	}

	/**
	 * Sets the "frequent_event_id_not" property to the appropriate value
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['frequent_event_id_not']
	 *                   though set it to FALSE if it is not set or invalid
	 *                   + FALSE = set the value to FALSE
	 *                   + integer = an integer, falling back to FALSE if
	 *                   the input is is invalid
	 *                   + array of integers = if each element is not an integer
	 *                   then the value will be set to FALSE
	 * @return void
	 *
	 * @uses CalendarSolution::get_int_array_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$frequent_event_id_not  to store the data
	 *
	 * @since Method available since version 3.7
	 */
	public function set_frequent_event_id_not($in = null) {
		if ($in === null) {
			$out = $this->get_int_array_from_request('frequent_event_id_not');
		} else {
			if (!is_array($in)) {
				$in = array($in);
			}
			$out = array();
			foreach ($in as $value) {
				if (!is_scalar($value)
					|| !preg_match('/^\d{1,10}$/', $value))
				{
					$this->frequent_event_id = false;
					return;
				}
				$out[] = $value;
			}
		}

		$this->frequent_event_id_not = $out;
	}

	/**
	 * Sets the "from" property
	 *
	 * CalendarSolution_List::set_from() defaults to today.
	 * CalendarSolution_List_Calendar::set_from() defaults to the first day of
	 * today's month.
	 *
	 * NOTE: "from" is reset to "permit_history_date" if "from" is earlier than
	 * "permit_history_date"
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['from'] though uses
	 *                   the default if it is not set or invalid
	 *                   + TRUE = use value of $_REQUEST['from'] if it is set,
	 *                   use the default if it is invalid, use FALSE if not set
	 *                   + FALSE = set the value to FALSE
	 *                   + string = a date in YYYY-MM-DD format though uses
	 *                   the default if it is invalid
	 * @return void
	 *
	 * @see CalendarSolution_List_Calendar::set_from()
	 *
	 * @uses CalendarSolution::get_date_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$from  to store the data
	 * @uses CalendarSolution_List::$permit_history_date  for validation
	 */
	public function set_from($in = null) {
		if ($in === null) {
			$in = $this->get_date_from_request('from');
		} elseif ($in === true) {
			$in = $this->get_date_from_request('from');
			if ($in === null) {
				$this->from = false;
				return;
			}
		} elseif ($in === false) {
			$this->from = false;
			return;
		}

		try {
			$this->from = new DateTimeSolution($in);
		} catch (Exception $e) {
			$this->from = new DateTimeSolution;
		}

		if ($this->from < $this->permit_history_date) {
			$this->from = $this->permit_history_date;
		}
	}

	/**
	 * Sets the "is_own_event" property to the appropriate value
	 *
	 * "Y" means only show events sponsored by your organization.
	 * "N" means only show events sponsored by other organizations.
	 * All events are shown if this is empty/unset/false.
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['is_own_event']
	 *                   though set it to FALSE if it is not set or invalid
	 *                   + FALSE = set the value to FALSE
	 *                   + "Y" or "N" = falling back to FALSE if
	 *                   the input is is invalid
	 * @return void
	 *
	 * @uses CalendarSolution::get_string_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$is_own_event  to store the data
	 *
	 * @since Method available since version 3.0
	 */
	public function set_is_own_event($in = null) {
		if ($in === null) {
			$in = $this->get_string_from_request('is_own_event');
		}
		if (!is_scalar($in) || !preg_match('/^[YN]$/', $in)) {
			$in = false;
		}

		$this->is_own_event = $in;
	}

	/**
	 * Sets the "limit_quantity" and "limit_start" properties
	 *
	 * Intended for use with the "QuickTable" and "Title" formats.
	 *
	 * To have lists show only the first ten events: <kbd>set_limit(10)</kbd>
	 *
	 * To have lists show the first ten events and possibly show navigation
	 * to later events: <kbd>set_limit(10, null)</kbd>.  One must also call
	 * <kbd>get_limit_navigation()</kbd> to get the navigation to show up.
	 *
	 * @param mixed $quantity  the number of rows to show
	 *              + NULL = use value of $_REQUEST['limit_quantity']
	 *              though set it to FALSE if it is not set or invalid
	 *              + FALSE = set the value to FALSE
	 *              + integer = an integer, falling back to FALSE if
	 *              the input is is invalid
	 * @param mixed $start  the row to start on.  Is zero indexed, so 0 starts
	 *              on the first row, 10 starts on the 11th row, etc.
	 *              + NULL = use value of $_REQUEST['limit_start']
	 *              though set it to 0 if it is not set or FALSE if invalid
	 *              + FALSE = set the value to FALSE
	 *              + integer = an integer, falling back to FALSE if
	 *              the input is is invalid
	 *              + Gets set to FALSE if $quantity is empty()
	 *
	 * @return void
	 *
	 * @see CalendarSolution_List::get_limit_navigation()
	 *
	 * @uses CalendarSolution::get_int_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::$limit_quantity  to store the data
	 * @uses CalendarSolution_List::$limit_start  to store the data
	 *
	 * @since $start parameter added in version 3.0
	 */
	public function set_limit($quantity, $start = false) {
		if ($quantity === null) {
			$quantity = $this->get_int_from_request('limit_quantity');
		}
		if (!is_scalar($quantity) || !preg_match('/^\d{1,10}$/', $quantity)) {
			$quantity = false;
		}
		$this->limit_quantity = $quantity;

		if (!$quantity || $start === false) {
			$this->limit_start = false;
			return;
		}

		if ($start === null) {
			$start = $this->get_int_from_request('limit_start');
			if ($start === null) {
				$this->limit_start = 0;
				return;
			}
		}
		if (!is_scalar($start) || !preg_match('/^\d{1,10}$/', $start)) {
			$start = false;
		}
		$this->limit_start = $start;
	}

	/**
	 * Sets the Featured Page "page_id" property to the appropriate value
	 *
	 * @param int $in  the featured page id to get the list for
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::$page_id  to store the data
	 */
	public function set_page_id($in) {
		if (!is_scalar($in) || !preg_match('/^\d{1,10}$/', $in)) {
			$in = false;
		}
		$this->page_id = $in;
	}

	/**
	 * Sets the furthest date in the future users are allowed to see
	 *
	 * NOTE: if the "to" property is later than this, "to" is reset to this.
	 *
	 * @param mixed $months  + integer = the number of months users can see,
	 *                         including the current month.  Default is 12.
	 *                         Falls back to the $months passed to
	 *                         CalendarSolution_List::__construct() if this
	 *                         parameter is less than or equal to that one.
	 *                       + FALSE = unlimited
	 * @return void
	 *
	 * @uses CalendarSolution_List::$permit_future_date  to store the data
	 * @uses CalendarSolution_List::interval_singleton()  if the value needs
	 *       to be set to the default
	 * @uses CalendarSolution_List::$to  for validation
	 *
	 * @since Method available since version 3.0
	 */
	public function set_permit_future_months($months = 12) {
		if ($months === false) {
			$this->permit_future_date = false;
			return;
		}

		$this->permit_future_date = new DateTimeSolution;
		$interval = $this->interval_singleton();
		if ($months > $interval->format('%m')) {
			$interval = new DateIntervalSolution('P' . ($months - 1) . 'M');
		}

		$this->permit_future_date->modify('first day of this month');
		$this->permit_future_date->add($interval);
		$this->permit_future_date->modify('last day of this month');

		if ($this->to > $this->permit_future_date) {
			$this->to = $this->permit_future_date;
		}
	}

	/**
	 * Sets the furthest date in the past users are allowed to see
	 *
	 * NOTE: if the "from" property is earlier than this, "from" is reset to
	 * this.
	 *
	 * @param mixed $months  + integer = the number of months users can see,
	 *                         including the current month.  Default is 12.
	 *                         0 = today, 1 = first day of this month.
	 *                       + FALSE = unlimited
	 * @return void
	 *
	 * @uses CalendarSolution_List::$permit_history_date  to store the data
	 * @uses CalendarSolution_List::$from  for validation
	 *
	 * @since Method available since version 3.0
	 */
	public function set_permit_history_months($months = 12) {
		if ($months === false) {
			$this->permit_history_date = false;
			return;
		}

		$this->permit_history_date = new DateTimeSolution;
		if ($months > 0) {
			$this->permit_history_date->modify('first day of this month');
			if ($months > 1) {
				$interval = new DateIntervalSolution('P' . ($months - 1) . 'M');
				$this->permit_history_date->sub($interval);
			}
		}

		if ($this->from < $this->permit_history_date) {
			$this->from = $this->permit_history_date;
		}
	}

	/**
	 * Sets the properties used later when generating the navigation elements
	 * for getting to earlier and later events
	 *
	 * NOTE: Does nothing if "$from" or "$to" are false.
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::$from  as the basis for the calculations
	 * @uses CalendarSolution_List::$to  as the basis for the calculations
	 * @uses CalendarSolution_List::interval_singleton()
	 * @uses CalendarSolution_List::$prior_from  for the "prior" link's from date
	 * @uses CalendarSolution_List::$prior_to  for the "prior" link's to date
	 * @uses CalendarSolution_List::$next_from  for the "next" link's from date
	 * @uses CalendarSolution_List::$next_to  for the "next" link's to date
	 * @uses CalendarSolution_List::set_permit_history_months() to set bounds on
	 *       how far back the dates can go
	 * @uses CalendarSolution_List::set_permit_future_months() to set bounds on
	 *       how far in the future the dates can go
	 *
	 * @throws CalendarSolution_Exception if "$from" or "$to" has not been set
	 */
	protected function set_prior_and_next_dates() {
		$this->called_set_prior_and_next_dates = true;

		if ($this->from === false || $this->to === false) {
			return;
		} elseif ($this->from === null || $this->to === null) {
			throw new CalendarSolution_Exception('set_from() and set_to()'
				. ' must be called before set_prior_and_next_dates()');
		}

		if ($this->permit_history_date === null) {
			$this->set_permit_history_months();
		}
		if ($this->permit_future_date === null) {
			$this->set_permit_future_months();
		}

		$one_day_interval = new DateIntervalSolution('P1D');

		$this->prior_to = new DateTimeSolution($this->from->format('Y-m-d'));
		$this->prior_to->sub($one_day_interval);

		$this->prior_from = new DateTimeSolution($this->prior_to->format('Y-m-d'));
		$this->prior_from->modify('first day of this month');
		$this->prior_from->sub($this->interval_singleton());

		if ($this->prior_from < $this->permit_history_date) {
			$this->prior_from = $this->permit_history_date;
		}
		if ($this->prior_to < $this->permit_history_date) {
			$this->prior_to = $this->permit_history_date;
		}

		$this->next_from = new DateTimeSolution($this->to->format('Y-m-d'));
		$this->next_from->add($one_day_interval);

		$this->next_to = new DateTimeSolution($this->next_from->format('Y-m-d'));
		$this->next_to->add($this->interval_singleton());
		$this->next_to->modify('last day of this month');

		if ($this->permit_future_date !== false
			&& $this->next_to > $this->permit_future_date)
		{
			$this->next_to = $this->permit_future_date;
		}
		if ($this->permit_future_date !== false
			&& $this->next_from > $this->permit_future_date)
		{
			$this->next_from = $this->permit_future_date;
		}
	}

	/**
	 * Sets all properties that can be populated with $_REQUEST data, but
	 * does so only for properties that have not been set yet
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List_Calendar::set_from()  to set the "from" date
	 *       when in Calendar view
	 * @uses CalendarSolution_List::set_from()  to set the "from" date
	 *       when in List view
	 * @uses CalendarSolution_List::set_to()  to set the "to" date
	 * @uses CalendarSolution_List::set_category_id()  to set the category id
	 * @uses CalendarSolution_List::set_frequent_event_id()  to set the frequent
	 *       event id
	 * @uses CalendarSolution_List::set_is_own_event()  to set the own event
	 *       flag
	 *
	 * @since Method available since version 3.0
	 */
	public function set_request_properties() {
		$this->called_set_request_properties = true;

		if ($this->from === null) {
			$this->set_from();
		}

		if ($this->to === null) {
			$this->set_to();
		}

		if ($this->category_id === null) {
			$this->set_category_id();
		}

		if ($this->category_id_not === null) {
			$this->set_category_id_not();
		}

		if ($this->frequent_event_id === null) {
			$this->set_frequent_event_id();
		}

		if ($this->frequent_event_id_not === null) {
			$this->set_frequent_event_id_not();
		}

		if ($this->is_own_event === null) {
			$this->set_is_own_event();
		}
	}

	/**
	 * Should cancelled events be shown or not?
	 *
	 * @param bool $in  pass FALSE to leave out cancelled events,
	 *                  is TRUE by default
	 * @return void
	 *
	 * @uses CalendarSolution_List::$show_cancelled  to store the decision
	 */
	public function set_show_cancelled($in) {
		$this->show_cancelled = (bool) $in;
	}

	/**
	 * Turns the Location field on or off when showing the "Calendar" format
	 *
	 * @param bool $in  pass FALSE to not show locations, is TRUE by default
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::$show_location  to store the decision
	 *
	 * @since Method available since version 3.0
	 *
	 * @internal This is in the CalendarSolution_List class to avoid method not
	 * found errors when users switch between Calendar and List views
	 */
	public function set_show_location($in) {
		$this->show_location = (bool) $in;
	}

	/**
	 * Show your own events before events produced by other organizations?
	 *
	 * Items are normally sorted by date then start time.  Enabling this option
	 * still sorts items by date, but within each day your events are shown
	 * first, sorted by time, then events by organizations are shown sorted by
	 * time.
	 *
	 * @param bool $in  pass TRUE to show your own events first,
	 *                  is FALSE by default
	 * @return void
	 *
	 * @uses CalendarSolution_List::$show_own_events_first  to store the decision
	 *
	 * @since Method available since version 3.0
	 */
	public function set_show_own_events_first($in) {
		$this->show_own_events_first = (bool) $in;
	}

	/**
	 * Turns the Summary field on or off when showing the "List" format
	 *
	 * @param bool $in  pass FALSE to not show summaries, is TRUE by default
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::$show_summary  to store the decision
	 *
	 * @internal This is in the CalendarSolution_List class to avoid method not
	 * found errors when users switch between Calendar and List views
	 */
	public function set_show_summary($in) {
		$this->show_summary = (bool) $in;
	}

	/**
	 * Sets the time format to be used by our format_date() method
	 *
	 * @param int $in  the format used by PHP's date() function
	 *
	 * @return void
	 *
	 * @uses CalendarSolution_List::$time_format  to store the data
	 * @see CalendarSolution::format_date()
	 *
	 * @since Method available since version 3.0
	 */
	public function set_time_format($in) {
		$this->time_format = $in;
	}

	/**
	 * Sets the "to" property (defaults to the last day of the month
	 * occurring $interval_spec months from today)
	 *
	 * NOTE: "to" is reset to "permit_future_date" if "to" is later than
	 * "permit_future_date".
	 *
	 * @param mixed $in  + NULL = use value of $_REQUEST['to'] though uses
	 *                   the default if it is not set or invalid
	 *                   + TRUE = use value of $_REQUEST['to'] if it is set,
	 *                   use the default if it is invalid, use FALSE if not set
	 *                   + FALSE = set the value to FALSE
	 *                   + string = a date in YYYY-MM-DD format though uses
	 *                   the default if it is invalid
	 * @return void
	 *
	 * @uses CalendarSolution_List::$to  to store the data
	 * @uses CalendarSolution::get_date_from_request()  to determine the
	 *       user's intention
	 * @uses CalendarSolution_List::interval_singleton()  if the value needs
	 *       to be set to the default
	 * @uses CalendarSolution_List::$permit_future_date  for validation
	 */
	public function set_to($in = null) {
		if ($in === null) {
			$in = $this->get_date_from_request('to');
		} elseif ($in === true) {
			$in = $this->get_date_from_request('to');
			if ($in === null) {
				$this->to = false;
				return;
			}
		} elseif ($in === false) {
			$this->to = false;
			return;
		}

		if ($in) {
			$add_months = false;
		} else {
			$add_months = true;
		}

		try {
			$this->to = new DateTimeSolution($in);
		} catch (Exception $e) {
			$add_months = true;
			$this->to = new DateTimeSolution;
		}

		if ($add_months) {
			$this->to->modify('first day of this month');
			$this->to->add($this->interval_singleton());
			$this->to->modify('last day of this month');
		}

		if ($this->permit_future_date && $this->to > $this->permit_future_date) {
			$this->to = $this->permit_future_date;
		}
	}

	/**
	 * Generates the SQL WHERE clause and cache keys for data retrieval
	 *
	 * This has been separated out from run_query() to facilitate cache
	 * lookups by the various get_rendering() methods.
	 *
	 * @uses CalendarSolution_List::$where_sql  to hold the WHERE clause
	 * @uses CalendarSolution_List::$cache_key  to indicate where the data
	 *       should be stored
	 * @uses CalendarSolution_List::$cache_count_key  to indicate where the row
	 *       count should be stored
	 * @uses CalendarSolution_List::$from  to know the start of the date range
	 * @uses CalendarSolution_List::$to  to know the end of the date range
	 * @uses CalendarSolution_List::$category_id  to limit entries to a
	 *       particular category if so desired
	 * @uses CalendarSolution_List::$category_id_not  to leave off particular
	 *       categories if so desired
	 * @uses CalendarSolution_List::$frequent_event_id  to limit entries to a
	 *       particular event if so desired
	 * @uses CalendarSolution_List::$frequent_event_id_not  to leave off
	 *       particular frequent events if so desired
	 * @uses CalendarSolution_List::$is_own_event  to limit entries to those
	 *       sponsored by you or those that are not
	 * @uses CalendarSolution_List::$page_id  to limit entries to those that
	 *       are set to be featured on the given page id
	 */
	protected function set_where_sql() {
		/*
		 * Establish WHERE elements.
		 */

		$where = array();

		if ($this->from && $this->to) {
			if ($this->from > $this->to) {
				throw new CalendarSolution_Exception("'from' can not be after 'to'");
			}

			$where[] = "date_start BETWEEN '"
				. $this->from->format('Y-m-d')
				. "' AND '" . $this->to->format('Y-m-d') . "'";
		} elseif ($this->from) {
			$where[] = "date_start >= '"
				. $this->from->format('Y-m-d') . "'";
		} elseif ($this->to) {
			$where[] = "date_start <= '"
				. $this->to->format('Y-m-d') . "'";
		}

		if ($this->category_id) {
			$where[] = "cs_calendar.category_id IN ("
				. implode(', ', $this->category_id) . ")";
		}

		if ($this->category_id_not) {
			$where[] = "cs_calendar.category_id NOT IN ("
				. implode(', ', $this->category_id_not) . ")";
		}

		if ($this->frequent_event_id) {
			$where[] = "cs_calendar.frequent_event_id = "
				. (int) $this->frequent_event_id;
		}

		if ($this->frequent_event_id_not) {
			$where[] = "cs_calendar.frequent_event_id NOT IN ("
				. implode(', ', $this->frequent_event_id_not) . ")";
		}

		if ($this->is_own_event) {
			$where[] = "is_own_event = '$this->is_own_event'";
		}

		if ($this->page_id) {
			if ($this->sql->SQLClassName == 'SQLSolution_PostgreSQLUser') {
				$where[] = "CAST(feature_on_page_id & " . (int) $this->page_id
					. " AS BOOLEAN)";
			} else {
				$where[] = "feature_on_page_id & " . (int) $this->page_id;
			}
		}

		if (!$this->show_cancelled) {
			$where[] = "cs_calendar.status_id <> "
				. $this->sql->Escape(__FILE__, __LINE__, self::STATUS_CANCELLED);
		}

		if ($where) {
			$this->where_sql = "\n WHERE " . implode(' AND ', $where);
		} else {
			$this->where_sql = '';
		}

		if ($this->use_cache) {
			$where_md5 = md5($this->where_sql);
			$this->cache_key = 'calendar_solution:' . $where_md5;

			if ($this->limit_quantity) {
				$this->cache_key .= ':' . $this->limit_quantity
					. '@' . (int) $this->limit_start;
				$this->cache_count_key = 'calendar_solution:count:' . $where_md5;
			}
		}
	}
}
Return current item: Calendar Solution