<?php /*

 ocPortal
 Copyright (c) ocProducts, 2004-2012

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license		http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright	ocProducts Ltd
 * @package		calendar
 */

/*
Note that all this code operates in "user time". That is, the timestamps being banded around are not as the server would recognise them, because they encode offsets to match the users timezone settings.
time() should never be called here, either explicitly, or implictly by not giving the date function a second argument. Instead, utctime_to_usertime() should be used to get the timestamp.
Any times that go-in-to or come-in-from the calendar backend API are also in "user time".
To complicate matters further "user time" is not the timestamp that would exist on a user's PC, as all timestamps are meant to be stored in GMT. "user time" is offsetted to compensate, a virtual construct.
*/

/**
 * Module page class.
 */
class Module_calendar
{

	/**
	 * Standard modular info function.
	 *
	 * @return ?array	Map of module info (NULL: module is disabled).
	 */
	function info()
	{
		$info=array();
		$info['author']='Chris Graham';
		$info['organisation']='ocProducts';
		$info['hacked_by']=NULL;
		$info['hack_version']=NULL;
		$info['version']=6;
		$info['locked']=false;
		$info['update_require_upgrade']=1;
		return $info;
	}

	/**
	 * Standard modular uninstall function.
	 */
	function uninstall()
	{
		$GLOBALS['SITE_DB']->drop_if_exists('calendar_events');
		$GLOBALS['SITE_DB']->drop_if_exists('calendar_types');
		$GLOBALS['SITE_DB']->drop_if_exists('calendar_reminders');
		$GLOBALS['SITE_DB']->drop_if_exists('calendar_interests');
		$GLOBALS['SITE_DB']->drop_if_exists('calendar_jobs');

		$perms=array('view_event_subscriptions','view_calendar','add_public_events','edit_viewable_events','edit_owned_events','view_personal_events','sense_personal_conflicts');
		foreach ($perms as $perm)
		{
			delete_specific_permission($perm);
		}

		delete_menu_item_simple('_SEARCH:calendar:type=misc:member_id={$USER_OVERIDE}');

		delete_config_option('calendar_show_stats_count_events');
		delete_config_option('calendar_show_stats_count_events_this_week');
		delete_config_option('calendar_show_stats_count_events_this_month');
		delete_config_option('calendar_show_stats_count_events_this_year');
	}

	/**
	 * Standard modular install function.
	 *
	 * @param  ?integer	What version we're upgrading from (NULL: new install)
	 * @param  ?integer	What hack version we're upgrading from (NULL: new-install/not-upgrading-from-a-hacked-version)
	 */
	function install($upgrade_from=NULL,$upgrade_from_hack=NULL)
	{
		require_lang('calendar');

		if (is_null($upgrade_from))
		{
			add_specific_permission('CALENDAR','view_calendar',true);
			add_specific_permission('CALENDAR','add_public_events',true);
			add_specific_permission('CALENDAR','view_personal_events',false,true);
			add_specific_permission('CALENDAR','sense_personal_conflicts',false);
			add_specific_permission('CALENDAR','view_event_subscriptions',false);

			$GLOBALS['SITE_DB']->create_table('calendar_events',array(
				'id'=>'*AUTO',
				'e_submitter'=>'USER',
				'e_views'=>'INTEGER',
				'e_title'=>'SHORT_TRANS',
				'e_content'=>'LONG_TRANS',
				'e_add_date'=>'TIME',
				'e_edit_date'=>'?TIME',
				'e_recurrence'=>'ID_TEXT', // [none, daily, weekly, monthly, yearly, xth_dotw_of_monthly] X [fractional-occurrence]. e.g. "daily yyyyynn" for weekdays
				'e_recurrences'=>'?INTEGER', // NULL means none/infinite
				'e_seg_recurrences'=>'BINARY',
				'e_start_year'=>'INTEGER',
				'e_start_month'=>'INTEGER',
				'e_start_day'=>'INTEGER',
				'e_start_hour'=>'?INTEGER',
				'e_start_minute'=>'?INTEGER',
				'e_end_year'=>'?INTEGER',
				'e_end_month'=>'?INTEGER',
				'e_end_day'=>'?INTEGER',
				'e_end_hour'=>'?INTEGER',
				'e_end_minute'=>'?INTEGER',
				'e_timezone'=>'ID_TEXT', // The settings above are stored in GMT, were converted from this timezone, and back to this timezone if e_do_timezone_conv==1
				'e_do_timezone_conv'=>'BINARY',
				'e_is_public'=>'BINARY',
				'e_priority'=>'INTEGER', // 1-5, like e-mail
				'allow_rating'=>'BINARY',
				'allow_comments'=>'SHORT_INTEGER',
				'allow_trackbacks'=>'BINARY',
				'notes'=>'LONG_TEXT',
				'e_type'=>'AUTO_LINK',
				'validated'=>'BINARY'
			));

			$GLOBALS['SITE_DB']->create_index('calendar_events','e_views',array('e_views'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','ces',array('e_submitter'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','publicevents',array('e_is_public'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','e_type',array('e_type'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','eventat',array('e_start_year','e_start_month','e_start_day','e_start_hour','e_start_minute'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','e_add_date',array('e_add_date'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','validated',array('validated'));

			$GLOBALS['SITE_DB']->create_table('calendar_types',array(
				'id'=>'*AUTO',
				't_title'=>'SHORT_TRANS',
				't_logo'=>'SHORT_TEXT',
				't_external_feed'=>'URLPATH',
			));
			$default_types=array('system_command','general','birthday','public_holiday','vacation','appointment','commitment','anniversary');
			foreach ($default_types as $type)
			{
				$GLOBALS['SITE_DB']->query_insert('calendar_types',array('t_external_feed'=>'','t_title'=>lang_code_to_default_content('DEFAULT_CALENDAR_TYPE__'.$type),'t_logo'=>'calendar/'.$type));
			}

			$GLOBALS['SITE_DB']->create_table('calendar_reminders',array(
				'id'=>'*AUTO',
				'e_id'=>'AUTO_LINK',
				'n_member_id'=>'USER',
				'n_seconds_before'=>'INTEGER'
			));

			$GLOBALS['SITE_DB']->create_table('calendar_interests',array(
				'i_member_id'=>'*USER',
				't_type'=>'*AUTO_LINK'
			));
		}

		if ((is_null($upgrade_from)) || ($upgrade_from<3))
		{
			$GLOBALS['SITE_DB']->create_table('calendar_jobs',array(
				'id'=>'*AUTO',
				'j_time'=>'TIME',
				'j_reminder_id'=>'?AUTO_LINK',
				'j_member_id'=>'?USER',
				'j_event_id'=>'AUTO_LINK'
			));

			$GLOBALS['SITE_DB']->create_index('calendar_jobs','applicablejobs',array('j_time'));
		}

		if ((!is_null($upgrade_from)) && ($upgrade_from<3))
		{
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_edit_date','?TIME');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_end_minute','?INTEGER');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_end_hour','?INTEGER');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_end_day','?INTEGER');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_end_month','?INTEGER');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_end_year','?INTEGER');
		}

		if ((!is_null($upgrade_from)) && ($upgrade_from<4))
		{
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','allow_rating','BINARY',0);
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','allow_comments','SHORT_INTEGER',0);
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','allow_trackbacks','BINARY',0);
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','notes','LONG_TEXT');
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','validated','BINARY',1);
			delete_specific_permission('edit_owned_events');
			delete_specific_permission('edit_viewable_events');
			add_config_option('ADD_CALENDAR_EVENT','points_ADD_CALENDAR_EVENT','integer','return addon_installed(\'points\')?\'2\':NULL;','POINTS','COUNT_POINTS_GIVEN');
			$GLOBALS['SITE_DB']->rename_table('calendar_declarations_of_interest','calendar_interests');
		}

		if ((!is_null($upgrade_from)) && ($upgrade_from<5))
		{
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','e_seg_recurrences','BINARY',0);
		}

		if ((is_null($upgrade_from)) || ($upgrade_from<5))
		{
			add_config_option('EVENTS','calendar_show_stats_count_events','tick','return addon_installed(\'stats_block\')?\'0\':NULL;','BLOCKS','STATISTICS');
			add_config_option('_EVENTS_THIS_WEEK','calendar_show_stats_count_events_this_week','tick','return addon_installed(\'stats_block\')?\'0\':NULL;','BLOCKS','STATISTICS');
			add_config_option('_EVENTS_THIS_MONTH','calendar_show_stats_count_events_this_month','tick','return addon_installed(\'stats_block\')?\'0\':NULL;','BLOCKS','STATISTICS');
			add_config_option('_EVENTS_THIS_YEAR','calendar_show_stats_count_events_this_year','tick','return addon_installed(\'stats_block\')?\'0\':NULL;','BLOCKS','STATISTICS');
			$GLOBALS['SITE_DB']->create_index('calendar_events','ftjoin_etitle',array('e_title'));
			$GLOBALS['SITE_DB']->create_index('calendar_events','ftjoin_econtent',array('e_content'));
		}

		if ((!is_null($upgrade_from)) && ($upgrade_from<6))
		{
			$GLOBALS['SITE_DB']->delete_table_field('calendar_events','e_geo_position');
			$GLOBALS['SITE_DB']->delete_table_field('calendar_events','e_groups_access');
			$GLOBALS['SITE_DB']->delete_table_field('calendar_events','e_groups_modify');
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','e_timezone','ID_TEXT');
			$GLOBALS['SITE_DB']->add_table_field('calendar_events','e_do_timezone_conv','BINARY');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_start_hour','?INTEGER');
			$GLOBALS['SITE_DB']->alter_table_field('calendar_events','e_start_minute','?INTEGER');

			$GLOBALS['SITE_DB']->add_table_field('calendar_types','t_external_feed','URLPATH');
		}

		if ((is_null($upgrade_from)) || ($upgrade_from<6))
		{
			$admin_groups=$GLOBALS['FORUM_DRIVER']->get_super_admin_groups();
			$groups=$GLOBALS['FORUM_DRIVER']->get_usergroup_list(false,true);

			// Save in permissions for event type
			$types=$GLOBALS['SITE_DB']->query_select('calendar_types');
			foreach ($types as $type)
			{
				foreach (array_keys($groups) as $group_id)
				{
					if (in_array($group_id,$admin_groups)) continue;

					$GLOBALS['SITE_DB']->query_insert('group_category_access',array('module_the_name'=>'calendar','category_name'=>strval($type['id']),'group_id'=>$group_id));
				}
			}
		}
	}

	/**
	 * Standard modular entry-point finder function.
	 *
	 * @return ?array	A map of entry points (type-code=>language-code) (NULL: disabled).
	 */
	function get_entry_points()
	{
		return array('misc'=>'CALENDAR');
	}

	/**
	 * Standard modular page-link finder function (does not return the main entry-points that are not inside the tree).
	 *
	 * @param  ?integer  The number of tree levels to computer (NULL: no limit)
	 * @param  boolean	Whether to not return stuff that does not support permissions (unless it is underneath something that does).
	 * @param  ?string	Position to start at in the tree. Does not need to be respected. (NULL: from root)
	 * @param  boolean	Whether to avoid returning categories.
	 * @return ?array	 	A tuple: 1) full tree structure [made up of (pagelink, permission-module, permissions-id, title, children, ?entry point for the children, ?children permission module, ?whether there are children) OR a list of maps from a get_* function] 2) permissions-page 3) optional base entry-point for the tree 4) optional permission-module 5) optional permissions-id (NULL: disabled).
	 */
	function get_page_links($max_depth=NULL,$require_permission_support=false,$start_at=NULL,$dont_care_about_categories=false)
	{
		$permission_page='cms_calendar';

		$tree=array();
		if (!$require_permission_support)
		{
			$tree=array();
			$rows=$dont_care_about_categories?array():$GLOBALS['SITE_DB']->query_select('calendar_types c LEFT JOIN '.$GLOBALS['SITE_DB']->get_table_prefix().'translate t ON '.db_string_equal_to('language',user_lang()).' AND c.t_title=t.id',array('c.id','text_original','c.t_title'),NULL,'ORDER BY text_original');
			if (($max_depth>0) || (is_null($max_depth)))
			{
				foreach ($rows as $row)
				{
					if (!has_category_access($GLOBALS['FORUM_DRIVER']->get_guest_id(),'calendar',strval($row['id']))) continue;

					if (is_null($row['text_original'])) $row['text_original']=get_translated_text($row['t_title']);

					if (($row['id']!=db_get_first_id()) || (($GLOBALS['FORUM_DRIVER']->is_super_admin()) && (cron_installed()))) // Filters system commands
						$tree[]=array('_SELF:_SELF:type=misc:int_'.strval($row['id']).'=1','calendar',$row['id'],$row['text_original'],array());
				}
			}
		}

		return array($tree,$permission_page);
	}

	/**
	 * Standard modular new-style deep page-link finder function (does not return the main entry-points).
	 *
	 * @param  string  	Callback function to send discovered page-links to.
	 * @param  MEMBER		The member we are finding stuff for (we only find what the member can view).
	 * @param  integer	Code for how deep we are tunnelling down, in terms of whether we are getting entries as well as categories.
	 * @param  string		Stub used to create page-links. This is passed in because we don't want to assume a zone or page name within this function.
	 * @param  ?string	Where we're looking under (NULL: root of tree). We typically will NOT show a root node as there's often already an entry-point representing it.
	 * @param  integer	Our recursion depth (used to calculate importance of page-link, used for instance by Google sitemap). Deeper is typically less important.
	 * @param  ?array		Non-standard for API [extra parameter tacked on] (NULL: yet unknown). Contents of database table for performance.
	 * @param  ?array		Non-standard for API [extra parameter tacked on] (NULL: yet unknown). Contents of database table for performance.
	 */
	function get_sitemap_pagelinks($callback,$member_id,$depth,$pagelink_stub,$parent_pagelink=NULL,$recurse_level=0,$category_data=NULL,$entry_data=NULL)
	{
		// We read in all data for efficiency
		if (is_null($category_data))
			$category_data=$GLOBALS['SITE_DB']->query_select('calendar_types c LEFT JOIN '.get_table_prefix().'translate t ON '.db_string_equal_to('language',user_lang()).' AND t.id=c.t_title',array('c.t_title','c.id','t.text_original AS title'));

		// This is where we start
		if (is_null($parent_pagelink))
		{
			$parent_pagelink=$pagelink_stub.':misc'; // This is the entry-point we're under

			// Subcategories
			foreach ($category_data as $row)
			{
				if ($row['id']==db_get_first_id()) continue;

				if (!has_category_access($GLOBALS['FORUM_DRIVER']->get_guest_id(),'calendar',strval($row['id']))) continue;

				if (is_null($row['title'])) $row['title']=get_translated_text($row['t_title']);

				$pagelink=$pagelink_stub.'misc:int_'.strval($row['id']).'=1';
				if (__CLASS__!='')
				{
					$this->get_sitemap_pagelinks($callback,$member_id,$depth,$pagelink_stub,$pagelink,$recurse_level+1,$category_data,$entry_data); // Recurse
				} else
				{
					call_user_func_array(__FUNCTION__,array($callback,$member_id,$depth,$pagelink_stub,$pagelink,$recurse_level+1,$category_data,$entry_data)); // Recurse
				}
				call_user_func_array($callback,array($pagelink,$parent_pagelink,NULL,NULL,max(0.7-$recurse_level*0.1,0.3),$row['title'])); // Callback
			}
		} else
		{
			list(,$parent_attributes,)=page_link_decode($parent_pagelink);

			// Entries
			if ($depth>=DEPTH__ENTRIES)
			{
				$start=0;
				do
				{
					$parent_id=db_get_first_id();
					foreach ($parent_attributes as $key=>$val)
					{
						if (substr($key,0,4)=='int_') $parent_id=intval(substr($key,4));
					}
					$entry_data=$GLOBALS['SITE_DB']->query_select('calendar_events d LEFT JOIN '.get_table_prefix().'translate t ON '.db_string_equal_to('language',user_lang()).' AND t.id=d.e_title',array('d.e_title','d.id','t.text_original AS title','e_type AS category_id','e_add_date AS add_date','e_edit_date AS edit_date'),array('e_type'=>intval($parent_id)),'',500,$start);

					foreach ($entry_data as $row)
					{
						if (!has_category_access($GLOBALS['FORUM_DRIVER']->get_guest_id(),'calendar',strval($row['category_id']))) continue;

						if (is_null($row['title'])) $row['title']=get_translated_text($row['e_title']);

						$pagelink=$pagelink_stub.'view:'.strval($row['id']);
						call_user_func_array($callback,array($pagelink,$parent_pagelink,$row['add_date'],$row['edit_date'],0.2,$row['title'])); // Callback
					}

					$start+=500;
				}
				while (array_key_exists(0,$entry_data));
			}
		}
	}

	/**
	 * Standard modular run function.
	 *
	 * @return tempcode	The result of execution.
	 */
	function run()
	{
		require_lang('calendar');
		require_lang('dates');
		require_code('calendar');
		require_css('calendar');
		require_code('feedback');

		$type=get_param('type',post_param('type','misc'));

		// Decide what to do
		if ($type=='declare_interest') return $this->declare_interest();
		if ($type=='undeclare_interest') return $this->undeclare_interest();
		if ($type=='unsubscribe_event') return $this->unsubscribe_event();
		if ($type=='interests') return $this->interests();
		if ($type=='subscribe_event') return $this->subscribe_event();
		if ($type=='_subscribe_event') return $this->_subscribe_event();
		if ($type=='misc') return $this->view_calendar();
		if ($type=='view') return $this->view_event();

		return new ocp_tempcode();
	}

	/**
	 * Gets the type filter, if there is one.
	 *
	 * @return array			The filter
	 */
	function get_filter()
	{
		$filter=array();
		$some_pos=false;
		$types=$GLOBALS['SITE_DB']->query_select('calendar_types',array('id'));
		foreach ($types as $type)
		{
			$t=$type['id'];
			$filter['int_'.strval($t)]=get_param_integer('int_'.strval($t),0);

			global $NON_CANONICAL_PARAMS;
			$NON_CANONICAL_PARAMS[]='int_'.strval($t);

			if (post_param_integer('id',NULL)===$type['id']) $filter['int_'.strval($t)]=1;
			if ($filter['int_'.strval($t)]==1) $some_pos=true;
		}
		return $some_pos?$filter:array();
	}

	/**
	 * Gets the type filter in a simple list form.
	 *
	 * @return array			The filter
	 */
	function get_and_filter()
	{
		$and_filter=array();
		$filter=$this->get_filter();
		foreach ($filter as $key=>$val)
		{
			if ($val==1)
				$and_filter[]=intval(substr($key,4));
		}
		return $and_filter;
	}

	/**
	 * View the main calendar screen, with certain filter allowances.
	 *
	 * @return tempcode		The UI
	 */
	function view_calendar()
	{
		check_specific_permission('view_calendar');

		$view=get_param('view','day');
		$member_id=get_param_integer('member_id',get_member());
		$filter=$this->get_filter();
		$GLOBALS['FEED_URL']=find_script('backend').'?mode=calendar&filter='.implode(',',$this->get_and_filter());
		if ($member_id!=get_member()) enforce_personal_access($member_id);
		$back_url=NULL;

		switch ($view)
		{
			case 'day': // Like a diary
				$id=get_param('id',date('Y-m-d',utctime_to_usertime()));
				if (strpos($id,'-')===false) $id=date('Y-m-d',utctime_to_usertime()); // The id was actually a filter, will need to use default date/time
				$self_encompassing=($id==date('Y-m-d',utctime_to_usertime()));
				$date=$id;
				$explode=explode('-',$id);
				if (count($explode)!=3) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
				$main=$this->view_calendar_view_day($id,$date,$explode,$member_id,$filter);
				$timestamp=mktime(0,0,0,intval($explode[1]),intval($explode[2]),intval($explode[0]));
				$back=get_week_number_for(utctime_to_usertime($timestamp));
				$back_view='week';
				$previous_timestamp=mktime(0,0,0,intval($explode[1]),intval($explode[2]),intval($explode[0]))-60*60*24;
				$previous=date('Y-m-d',$previous_timestamp);
				$next_timestamp=mktime(0,0,0,intval($explode[1]),intval($explode[2]),intval($explode[0]))+60*60*24;
				$next=date('Y-m-d',$next_timestamp);
				$title=get_page_title('CALENDAR_SPECIFIC',true,array(escape_html(locale_filter(date(do_lang('calendar_date_verbose'),$timestamp)))));
				break;
			case 'week': // Like a compressed diary
				$id=get_param('id',get_week_number_for(utctime_to_usertime()));
				$self_encompassing=($id==get_week_number_for(utctime_to_usertime()));
				$explode=explode('-',$id);
				if (count($explode)!=2) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
				list($start_month,$start_day,$start_year)=date_from_week_of_year(intval($explode[0]),intval($explode[1]));
				$date=strval($start_year).'-'.strval($start_month).'-01';
				$main=$this->view_calendar_view_week($id,$date,$explode,$member_id,$filter);
				$timestamp=mktime(0,0,0,$start_month,$start_day,$start_year);
				$back=$explode[0].'-'.strval($start_month);
				$back_view='month';
				$previous_timestamp=mktime(0,0,0,$start_month,$start_day,$start_year)-60*60*24*7;
				$previous=get_week_number_for($previous_timestamp);
				$next_timestamp=mktime(0,0,0,$start_month,$start_day,$start_year)+60*60*24*7;
				$next=get_week_number_for($next_timestamp);
				$title=get_page_title('CALENDAR_SPECIFIC_WEEK',true,array(escape_html($explode[0]),escape_html($explode[1]),escape_html(locale_filter(date('M',$timestamp)))));
				break;
			case 'month': // Like a main calendar page
				$id=get_param('id',date('Y-m',utctime_to_usertime()));
				$self_encompassing=($id==date('Y-m',utctime_to_usertime()));
				$date=$id.'-01';
				$explode=explode('-',$id);
				if (count($explode)!=2) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
				$main=$this->view_calendar_view_month($id,$date,$explode,$member_id,$filter);
				$timestamp=mktime(0,0,0,intval($explode[1]),1,intval($explode[0]));
				$back=$explode[0];
				$back_view='year';
				$previous_month=intval($explode[1])-1;
				$previous_year=intval($explode[0]);
				if ($previous_month==0)
				{
					$previous_month=12;
					$previous_year=$previous_year-1;
				}
				$next_month=intval($explode[1])+1;
				$next_year=intval($explode[0]);
				if ($next_month==13)
				{
					$next_month=1;
					$next_year=$next_year+1;
				}
				$previous_timestamp=mktime(0,0,0,$previous_month,1,$previous_year);
				$previous=date('Y-m',$previous_timestamp);
				$next_timestamp=mktime(0,0,0,$next_month,1,$next_year);
				$next=date('Y-m',$next_timestamp);
				$title=get_page_title('CALENDAR_SPECIFIC',true,array(escape_html(locale_filter(my_strftime(do_lang('calendar_month_in_year_verbose'),$timestamp)))));
				break;
			case 'year': // Like front of a calendar
				$id=get_param('id',date('Y'));
				$self_encompassing=($id==date('Y'));
				$date=$id.'-01-01';
				$explode=explode('-',$id);
				if (count($explode)!=1) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
				$main=$this->view_calendar_view_year($id,$date,$explode,$member_id,$filter);
				$timestamp=mktime(0,0,0,1,1,intval($id));
				$back_url=$GLOBALS['FORUM_DRIVER']->member_profile_url($member_id);
				$previous_timestamp=mktime(0,0,0,1,1,intval($explode[0])-1);
				$previous=date('Y',$previous_timestamp);
				$next_timestamp=mktime(0,0,0,1,1,intval($explode[0])+1);
				$next=date('Y',$next_timestamp);
				$title=get_page_title('CALENDAR_SPECIFIC',true,array(escape_html($id)));
				break;
			default:
				warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
		}

		// Nofollow stuff
		$previous_no_follow=($previous_timestamp<filectime(get_file_base().'/info.php'));
		$next_no_follow=($next_timestamp>time());

		$map=array_merge($filter,array('page'=>'_SELF','view'=>$view,'id'=>$previous));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$previous_url=build_url($map,'_SELF');
		$map=array_merge($filter,array('page'=>'_SELF','view'=>$view,'id'=>$next));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$next_url=build_url($map,'_SELF');
		if (is_null($back_url))
		{
			$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>$back_view,'id'=>$back));
			if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
			$back_url=build_url($map,'_SELF');
		}

		$interests_url=build_url(array('page'=>'_SELF','type'=>'interests','view'=>$view,'id'=>$id),'_SELF');
		$event_types_1=new ocp_tempcode();
		$types=$GLOBALS['SITE_DB']->query_select('calendar_types',array('id','t_title'));
		foreach ($types as $type)
		{
			if ($type['id']==db_get_first_id()) continue;
			if (!has_category_access(get_member(),'calendar',strval($type['id']))) continue;

			if (is_guest())
			{
				$interested='';
			} else
			{
				$test=$GLOBALS['SITE_DB']->query_value_null_ok('calendar_interests','i_member_id',array('i_member_id'=>get_member(),'t_type'=>$type['id']));
				$interested=(!is_null($test))?'not_interested':'interested';
			}
			$event_types_1->attach(do_template('CALENDAR_EVENT_TYPE',array('_GUID'=>'104b723d5211f400267345f616c4a677','S'=>'I','INTERESTED'=>$interested,'TYPE'=>get_translated_text($type['t_title']),'TYPE_ID'=>strval($type['id']))));
		}
		$filter_url=build_url(array('page'=>'_SELF','type'=>'misc','view'=>$view,'id'=>$id),'_SELF',NULL,false,true);
		$event_types_2=new ocp_tempcode();
		foreach ($types as $type)
		{
			if ($type['id']==db_get_first_id()) continue;
			if (!has_category_access(get_member(),'calendar',strval($type['id']))) continue;

			$interested=((count($filter)==0) || ($filter['int_'.strval($type['id'])]==1))?'not_interested':'interested';
			$event_types_2->attach(do_template('CALENDAR_EVENT_TYPE',array('_GUID'=>'7511d60148835b7f4fea68a246af424e','S'=>'F','INTERESTED'=>$interested,'TYPE'=>get_translated_text($type['t_title']),'TYPE_ID'=>strval($type['id']))));
		}

		if ((has_actual_page_access(NULL,'cms_calendar',NULL,NULL)) && (has_submit_permission('low',get_member(),get_ip_address(),'cms_calendar')))
		{
			$and_filter=$this->get_and_filter();
			$add_url=build_url(array('page'=>'cms_calendar','type'=>'ad','date'=>$self_encompassing?NULL:$date,'e_type'=>(count($and_filter)==1)?$and_filter[0]:NULL),get_module_zone('cms_calendar'));
		} else
		{
			$add_url=new ocp_tempcode();
		}

		// Allow jumping between views
		if ($self_encompassing) $timestamp=time();
		$day=date('Y-m-d',$timestamp);
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$day));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$day_url=($view=='day')?new ocp_tempcode():build_url($map,'_SELF');
		$week=get_week_number_for($timestamp);
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'week','id'=>$week));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$week_url=($view=='week')?new ocp_tempcode():build_url($map,'_SELF');
		$month=date('Y-m',$timestamp);
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$month));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$month_url=($view=='month')?new ocp_tempcode():build_url($map,'_SELF');
		$year=date('Y',$timestamp);
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'year','id'=>$year));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$year_url=($view=='year')?new ocp_tempcode():build_url($map,'_SELF');

		// RSS
		$fields=new ocp_tempcode();
		require_code('form_templates');
		for ($i=0;$i<10;$i++)
		{
			$fields->attach(form_input_line(do_lang_tempcode('FEED',integer_format($i+1)),'','feed_'.strval($i),ocp_admirecookie('feed_'.strval($i)),false));
		}
		$rss_form=do_template('FORM',array('HIDDEN'=>'','TEXT'=>do_lang_tempcode('DESCRIPTION_FEEDS_TO_OVERLAY'),'URL'=>get_self_url(),'FIELDS'=>$fields,'SUBMIT_NAME'=>do_lang_tempcode('PROCEED')));

		return do_template('CALENDAR_MAIN_SCREEN',array('_GUID'=>'147a58dbe05366ac37698a8cdd501d12','RSS_FORM'=>$rss_form,'PREVIOUS_NO_FOLLOW'=>$previous_no_follow,'NEXT_NO_FOLLOW'=>$next_no_follow,'DAY_URL'=>$day_url,'WEEK_URL'=>$week_url,'MONTH_URL'=>$month_url,'YEAR_URL'=>$year_url,'PREVIOUS_URL'=>$previous_url,'NEXT_URL'=>$next_url,'ADD_URL'=>$add_url,'TITLE'=>$title,'BACK_URL'=>$back_url,'MAIN'=>$main,'FILTER_URL'=>$filter_url,'EVENT_TYPES_1'=>$event_types_1,'INTERESTS_URL'=>$interests_url,'EVENT_TYPES_2'=>$event_types_2));
	}

	/**
	 * The calendar area view for viewing a single day.
	 *
	 * @param  string			The day we are viewing
	 * @param  string			The day (Y-m-d) we are viewing
	 * @param  array			List of components of our viewed ID
	 * @param  MEMBER			The member ID we are viewing as
	 * @param  ?array			The type filter (NULL: none)
	 * @return tempcode		The UI
	 */
	function view_calendar_view_day($view_id,$day,$explode,$member_id,$filter)
	{
		$start_year=intval($explode[0]);
		$start_month=intval($explode[1]);
		$start_day=intval($explode[2]);

		$period_start=mktime(0,0,0,$start_month,$start_day,$start_year);

		$period_end=mktime(0,0,0,$start_month,$start_day+1,$start_year);
		$happenings=calendar_matches($member_id,true,$period_start,$period_end,$filter);

		global $M_SORT_KEY;
		$M_SORT_KEY=0;
		usort($happenings,'multi_sort');

		$hours=new ocp_tempcode();
		$streams=array(array());
		foreach ($happenings as $happening)
		{
			list($e_id,$event,$from,$to,$real_from,$real_to,$utc_real_from)=$happening;
			$date=is_null($event['e_start_hour'])?'':locale_filter(my_strftime(do_lang('calendar_minute'),$from));
			if (is_numeric($e_id))
			{
				$map=array_merge($filter,array('page'=>'_SELF','type'=>'view','id'=>$event['e_id'],'day'=>date('Y-m-d',$utc_real_from),'date'=>$view_id));
				if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
				$url=build_url($map,'_SELF');
			} else
			{
				$url=$e_id;
			}
			$icon=$event['t_logo'];
			$from_h=intval(date('H',$from));
			if (!is_null($to))
			{
				$date=date_range($real_from,$real_to,!is_null($event['e_start_hour']));
				//$date2=date('H:i',$to);
				if ($to>=$period_end-60/*1 minute gap we use to stop stuff spanning to start of next day*/)
				{
					$to_h=24;
				} else
				{
					$to_h=intval(date('H',$to));
					if (date('i',$to)!='0') $to_h++;
				}
			} else $to_h=$from_h;

			// Try and find us a stream that can fit us
			$found_stream=NULL;
			foreach ($streams as $i=>$stream)
			{
				for ($h=$from_h;$h<=$to_h;$h++)
				{
					if (array_key_exists($h,$stream)) break;
				}
				if (($h==$to_h+1) && (!array_key_exists($h-1,$stream)))
				{
					$found_stream=$i;
					break;
				}
			}
			if (is_null($found_stream))
			{
				$streams[]=array();
				$found_stream=count($streams)-1;
			}

			// Fill in stream gaps as appropriate
			$title=is_integer($event['e_title'])?get_translated_text($event['e_title']):$event['e_title'];
			$down=strval($to_h-$from_h);
			$description=(intval($down)<3)?new ocp_tempcode():(is_numeric($event['e_content'])?get_translated_tempcode($event['e_content']):$event['e_content']);
			$priority_lang=do_lang_tempcode('PRIORITY_'.strval($event['e_priority']));
			$priority_icon='calendar/priority_'.strval($event['e_priority']);
			$streams[$found_stream][$from_h]=array('TPL'=>'CALENDAR_DAY_ENTRY','DESCRIPTION'=>$description,'DOWN'=>$down,'ID'=>strval($event['e_id']),'T_TITLE'=>array_key_exists('t_title',$event)?(is_string($event['t_title'])?$event['t_title']:get_translated_text($event['t_title'])):'RSS','PRIORITY'=>strval($event['e_priority']),'ICON'=>$icon,'TIME'=>$date,'TITLE'=>$title,'URL'=>$url,'PRIORITY_LANG'=>$priority_lang,'PRIORITY_ICON'=>$priority_icon,'RECURRING'=>$event['e_recurrence']!='none');
			for ($h=$from_h+1;$h<$to_h;$h++)
			{
				$streams[$found_stream][$h]=array('TPL'=>'-1');
			}
		}

		for ($i=0;$i<24;$i++)
		{
			// Work out the hour
			$hour=locale_filter(date(do_lang('time_hour_and_mins'),$i*60*60));

			// The streams need rendering for each hour
			$_streams=new ocp_tempcode();
			foreach ($streams as $stream)
			{
				$down='1';
				$priority='free_time';
				$entry=mixed();

				if (!array_key_exists($i,$stream))
				{
					$class='free_time_hourly';
					$text='&nbsp;';
					$entry=static_evaluate_tempcode(do_template('CALENDAR_DAY_ENTRY_FREE',array('_GUID'=>'0091fbb877164ac797cb88b4571b5d35','CLASS'=>$class,'TEXT'=>$text))); /*HTMLHTML*/
				} else
				{
					if ($stream[$i]['TPL']!='-1')
					{
						$down=$stream[$i]['DOWN'];
						$priority=$stream[$i]['PRIORITY'];
						$entry=static_evaluate_tempcode(do_template($stream[$i]['TPL'],$stream[$i])); /*HTMLHTML*/
					}
					else $entry='';
				}

				if ($down=='0') $down='1';
				if /*((is_object($entry)) && (!$entry->is_empty()))*/($entry!='')
				{
					$timestamp=$period_start+$i*60*60;
					if ((has_actual_page_access(NULL,'cms_calendar',NULL,NULL)) && (has_submit_permission('low',get_member(),get_ip_address(),'cms_calendar')))
					{
						$and_filter=$this->get_and_filter();
						$add_url=build_url(array('page'=>'cms_calendar','type'=>'ad','date'=>date('Y-m-d H:i:s',$timestamp),'e_type'=>(count($and_filter)==1)?$and_filter[0]:NULL),get_module_zone('cms_calendar'));
					} else $add_url=new ocp_tempcode();
					$_streams->attach(/*HTMLHTML*/static_evaluate_tempcode(do_template('CALENDAR_DAY_STREAM_HOUR',array('_GUID'=>'93a8fb53183a4225ec3bf7f2ea07cfc5','CURRENT'=>date('Y-m-d H',utctime_to_usertime())==date('Y-m-d H',$timestamp),'ADD_URL'=>$add_url,'PRIORITY'=>$priority,'DOWN'=>$down,'ENTRY'=>$entry))));
				}
			}

			$hours->attach(do_template('CALENDAR_DAY_HOUR',array('_GUID'=>'d967ce3f793942f78104c53b105f9f74','HOUR'=>$hour,'STREAMS'=>$_streams)));
		}

		return do_template('CALENDAR_DAY',array('_GUID'=>'60e102b38025c1b1618ac36070564065','HOURS'=>$hours,'PERIOD_START'=>strval($period_start),'PERIOD_END'=>strval($period_end)));
	}

	/**
	 * The calendar area view for viewing a single week.
	 *
	 * @param  string			The week we are viewing
	 * @param  string			The day (Y-m-d) we are viewing
	 * @param  array			List of components of our viewed ID
	 * @param  MEMBER			The member ID we are viewing as
	 * @param  ?array			The type filter (NULL: none)
	 * @return tempcode		The UI
	 */
	function view_calendar_view_week($view_id,$day,$explode,$member_id,$filter)
	{
		$start_year=intval($explode[0]);
		$start_week=intval($explode[1]);
		list($start_month,$start_day,$start_year)=date_from_week_of_year($start_year,$start_week);

		$period_start=mktime(0,0,0,$start_month,$start_day,$start_year);
		$period_end=$period_start+60*60*24*7;
		$happenings=calendar_matches($member_id,true,$period_start,$period_end,$filter);

		global $M_SORT_KEY;
		$M_SORT_KEY=0;
		usort($happenings,'multi_sort');

		if (get_option('ssw')=='0')
		{
			$day_remap=array('Mon'=>0,'Tue'=>1,'Wed'=>2,'Thu'=>3,'Fri'=>4,'Sat'=>5,'Sun'=>6);
		} else
		{
			$day_remap=array('Sun'=>6,'Mon'=>0,'Tue'=>1,'Wed'=>2,'Thu'=>3,'Fri'=>4,'Sat'=>5);
		}

		// We start with our 24x7 stream array, assuming all is free time
		$streams=array();  // hour X day
		for ($i=0;$i<24;$i++)
		{
			$streams[$i]=array();
			for ($j=0;$j<7;$j++)
			{
				$streams[$i][$j]=array('free_time','',$i);
			}
		}
		for ($j=0;$j<7;$j++)
		{
			for ($i=0;$i<24;$i++)
			{
				$entries=new ocp_tempcode();

				$class='';
				$worst_priority=6;
				$continuation=$i;

				// Happenings in a single day
				for ($hap_i=0;$hap_i<count($happenings);$hap_i++)
				{
					$happening=$happenings[$hap_i];
					list($e_id,$event,$from,$to,$real_from,$real_to,$utc_real_from)=$happening;
					$date=date('D:H',$from);
					$explode2=explode(':',$date);
					if ((intval($explode2[1])==$i) && ($day_remap[$explode2[0]]==$j))
					{
						$date=is_null($event['e_start_hour'])?'':locale_filter(my_strftime(do_lang('calendar_minute'),$real_from));
						if (is_numeric($e_id))
						{
							$map=array_merge($filter,array('page'=>'_SELF','type'=>'view','id'=>$event['e_id'],'day'=>date('Y-m-d',$utc_real_from),'date'=>$view_id,'back'=>'week'));
							if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
							$url=build_url($map,'_SELF');
						} else
						{
							$url=$e_id;
						}
						$icon=$event['t_logo'];
						if (!is_null($to))
						{
							$date=date_range($real_from,$real_to,!is_null($event['e_start_hour']));
							if ((!is_null($to)) && ($to>=mktime(0,0,0,$start_month,$start_day+$j+1,$start_year)))
							{
								$continuation=24;
								$ntime=mktime(0,0,0,$start_month,$start_day+$j+1,$start_year);
								if ($ntime<$period_end)
									$happenings[]=array($e_id,$event,$ntime,$to,$real_from,$real_to,$utc_real_from);
							}
							elseif (intval(date('H',$to))>$i+1)
							{
								$continuation=max($continuation,intval(date('H',$to)));
							}
						}
						$title=is_integer($event['e_title'])?get_translated_text($event['e_title']):$event['e_title'];
						$entries->attach(do_template('CALENDAR_WEEK_ENTRY',array('_GUID'=>'a5577fb634ecc5480789d1cd21f686fb','ID'=>strval($event['e_id']),'T_TITLE'=>array_key_exists('t_title',$event)?(is_string($event['t_title'])?$event['t_title']:get_translated_text($event['t_title'])):'RSS','PRIORITY'=>strval($event['e_priority']),'ICON'=>$icon,'TIME'=>$date,'TITLE'=>$title,'URL'=>$url,'RECURRING'=>$event['e_recurrence']!='none')));

						if ($event['e_priority']<$worst_priority)
						{
							$worst_priority=$event['e_priority'];
							$class='priority_'.strval($event['e_priority']);
						}
					}
				}

				// Not free time anymore
				if ($class!='')
				{
					$streams[$i][$j]=array($class,$entries,$continuation);

					// Now work backwards to cut back any continuation onto this spot
					for ($k=$i-1;$k>=0;$k--)
					{
						if ($streams[$k][$j][2]>=$i) $streams[$k][$j][2]=$i-1;
					}
				}
			}
		}

		// Now mark continuations ahead
		for ($i=0;$i<24;$i++)
		{
			for ($j=0;$j<7;$j++)
			{
				if ($streams[$i][$j][0]!='continuation')
				{
					$continuation=$streams[$i][$j][2];
					for ($k=$i+1;$k<=$continuation;$k++)
					{
						$streams[$k][$j][0]='continuation';
					}
				}
			}
		}

		// Now render
		$hours=new ocp_tempcode();
		for ($i=0;$i<24;$i++)
		{
			$hour=locale_filter(date(do_lang('time_hour_and_mins'),$i*60*60));

			$days=new ocp_tempcode();
			for ($j=0;$j<7;$j++)
			{
				$class=$streams[$i][$j][0];
				$entries=$streams[$i][$j][1];
				$continuation=$streams[$i][$j][2];

				if (((!is_object($entries)) || ($entries->is_empty())) && ($class!='continuation'))
				{
					$entries=do_template('CALENDAR_WEEK_ENTRY_FREE',array('_GUID'=>'4a16cdd5a2a0e5a444fd61a6aafa2ffa','CLASS'=>$class,'TEXT'=>''));
				}

				$down=$continuation-$i;

				if ($class!='continuation')
				{
					$timestamp=$period_start+($i+24*$j)*60*60;
					if ((has_actual_page_access(NULL,'cms_calendar',NULL,NULL)) && (has_submit_permission('low',get_member(),get_ip_address(),'cms_calendar')))
					{
						$and_filter=$this->get_and_filter();
						$add_url=build_url(array('page'=>'cms_calendar','type'=>'ad','date'=>date('Y-m-d H:i:s',$timestamp),'e_type'=>(count($and_filter)==1)?$and_filter[0]:NULL),get_module_zone('cms_calendar'));
					} else $add_url=new ocp_tempcode();
					$days->attach(do_template('CALENDAR_WEEK_HOUR_DAY',array('_GUID'=>'e001b4b2ea1995760ef0d4460d93b2e1','CURRENT'=>date('Y-m-d H',utctime_to_usertime())==date('Y-m-d H',$timestamp),'ADD_URL'=>$add_url,'DOWN'=>strval($down+1),'DAY'=>$day,'HOUR'=>$hour,'CLASS'=>$class,'ENTRIES'=>$entries)));
				}
			}

			$hours->attach(do_template('CALENDAR_WEEK_HOUR',array('_GUID'=>'a57d0d6a683d30fc0a48168b43299607','HOUR'=>$hour,'DAYS'=>$days)));
		}

		$offset=0;
		if (get_option('ssw')=='1')
		{
			$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
			$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
			if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
			$sunday_url=build_url($map,'_SELF');
			$sunday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
			$offset++;
		}
		$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$monday_url=build_url($map,'_SELF');
		$monday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
		$offset++;
		$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$tuesday_url=build_url($map,'_SELF');
		$tuesday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
		$offset++;
		$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$wednesday_url=build_url($map,'_SELF');
		$wednesday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
		$offset++;
		$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$thursday_url=build_url($map,'_SELF');
		$thursday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
		$offset++;
		$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$friday_url=build_url($map,'_SELF');
		$friday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
		$offset++;
		$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$saturday_url=build_url($map,'_SELF');
		$saturday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
		$offset++;
		if (get_option('ssw')=='0')
		{
			$datex=date('Y-m-d',mktime(0,0,0,$start_month,$start_day+$offset,intval($explode[0])));
			$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$datex));
			if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
			$sunday_url=build_url($map,'_SELF');
			$sunday_date=locale_filter(date(do_lang('calendar_day_of_month_verbose'),$period_start+$offset*60*60*24));
			$offset++;
		}

		return do_template('CALENDAR_WEEK',array('_GUID'=>'8dd9ad4e874d2f320a48551e0c9bde57','MONDAY_DATE'=>$monday_date,'TUESDAY_DATE'=>$tuesday_date,'WEDNESDAY_DATE'=>$wednesday_date,'THURSDAY_DATE'=>$thursday_date,'FRIDAY_DATE'=>$friday_date,'SATURDAY_DATE'=>$saturday_date,'SUNDAY_DATE'=>$sunday_date,'MONDAY_URL'=>$monday_url,'TUESDAY_URL'=>$tuesday_url,'WEDNESDAY_URL'=>$wednesday_url,'THURSDAY_URL'=>$thursday_url,'FRIDAY_URL'=>$friday_url,'SATURDAY_URL'=>$saturday_url,'SUNDAY_URL'=>$sunday_url,'HOURS'=>$hours,'PERIOD_START'=>strval($period_start),'PERIOD_END'=>strval($period_end)));
	}

	/**
	 * The calendar area view for viewing a single month.
	 *
	 * @param  string			The month we are viewing
	 * @param  string			The day (Y-m-d) we are viewing
	 * @param  array			List of components of our viewed ID
	 * @param  MEMBER			The member ID we are viewing as
	 * @param  ?array			The type filter (NULL: none)
	 * @return tempcode		The UI
	 */
	function view_calendar_view_month($view_id,$day,$explode,$member_id,$filter)
	{
		$period_start=mktime(0,0,0,intval($explode[1]),1,intval($explode[0]));
		$period_end=mktime(0,0,0,intval($explode[1])+1,1,intval($explode[0]));
		$_days=intval(round(floatval($period_end-$period_start)/floatval(60*60*24)));
		if ($_days==0) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));

		$happenings=calendar_matches($member_id,true,$period_start,$period_end,$filter);

		global $M_SORT_KEY;
		$M_SORT_KEY=0;
		usort($happenings,'multi_sort');

		if (get_option('ssw')=='0')
		{
			$ex_array=array('Mon'=>0,'Tue'=>1,'Wed'=>2,'Thu'=>3,'Fri'=>4,'Sat'=>5,'Sun'=>6);
		} else
		{
			$ex_array=array('Sun'=>0,'Mon'=>1,'Tue'=>2,'Wed'=>3,'Thu'=>4,'Fri'=>5,'Sat'=>6);
		}
		$empty_entry=do_template('CALENDAR_MONTH_ENTRY_FREE',array('_GUID'=>'c8eba771bd5f9a58d4822f4a2dac57a2','CLASS'=>'spacer','TEXT'=>''));

		$weeks=new ocp_tempcode();
		$days=new ocp_tempcode();
		$week_date=locale_filter(date(do_lang('calendar_day_of_month'),$period_start));
		$week_count=intval(get_week_number_for($period_start,true));
		$day_of_week=date('D',$period_start);
		for ($x=0;$x<$ex_array[$day_of_week];$x++)
			$days->attach(do_template('CALENDAR_MONTH_DAY',array('_GUID'=>'783ff6377292cc0400638c8857446a16','CURRENT'=>false,'DAY_URL'=>'','CLASS'=>'','DAY'=>'','ENTRIES'=>$empty_entry)));
		$dotw=$ex_array[$day_of_week]-1;
		for ($i=1;$i<=$_days;$i++)
		{
			$entries=new ocp_tempcode();

			$timestamp=$period_start+($i-1)*60*60*24+60*60;
			$day_of_week=date('D',$timestamp);
			if ($dotw==6)
			{
				$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'week','id'=>$explode[0].'-'.strval($week_count)));
				if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
				$week_url=build_url($map,'_SELF');
				$weeks->attach(do_template('CALENDAR_MONTH_WEEK',array('_GUID'=>'1010b12d8677bc577f30a013e9a838ce','WEEK_URL'=>$week_url,'WEEK_DATE'=>$week_date,'DAYS'=>$days)));
				$days=new ocp_tempcode();
				$week_date=locale_filter(date(do_lang('calendar_day_of_month'),$period_start+($i-1)*60*60*24));
				$week_count++;
				$dotw=0;
			} else $dotw++;

			$class='';
			$worst_priority=6;

			for ($hap_i=0;$hap_i<count($happenings);$hap_i++)
			{
				$happening=$happenings[$hap_i];

				list($e_id,$event,$from,$to,$real_from,$real_to,$utc_real_from)=$happening;
				$date=date('d',$from);
				if (intval($date)==$i)
				{
					$date=is_null($event['e_start_hour'])?'':locale_filter(my_strftime(do_lang('calendar_minute'),$real_from));

					if (is_numeric($e_id))
					{
						$map=array_merge($filter,array('page'=>'_SELF','type'=>'view','id'=>$event['e_id'],'day'=>date('Y-m-d',$utc_real_from),'date'=>$view_id,'back'=>'month'));
						if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
						$url=build_url($map,'_SELF');
					} else
					{
						$url=$e_id;
					}
					$icon=$event['t_logo'];
					if (!is_null($to))
					{
						$date=date_range($real_from,$real_to,!is_null($event['e_start_hour']));
					}
					$title=is_integer($event['e_title'])?get_translated_text($event['e_title']):$event['e_title'];
					$entries->attach(do_template('CALENDAR_MONTH_ENTRY',array('_GUID'=>'58353fc64595f981d41da303cfe40855','ID'=>strval($event['e_id']),'T_TITLE'=>array_key_exists('t_title',$event)?(is_string($event['t_title'])?$event['t_title']:get_translated_text($event['t_title'])):'RSS','PRIORITY'=>strval($event['e_priority']),'ICON'=>$icon,'TIME'=>$date,'TITLE'=>$title,'URL'=>$url,'RECURRING'=>$event['e_recurrence']!='none')));

					if ($event['e_priority']<$worst_priority)
					{
						$worst_priority=$event['e_priority'];
						$class='priority_'.strval($event['e_priority']);
					}

					if (!is_null($to))
					{
						$test=date('d',$to);
						$test2=date('d',$from);
						if (((intval($test)>intval($test2)) || (intval(date('m',$to))!=intval(date('m',$from))) || (intval(date('Y',$to))!=intval(date('Y',$from)))))
						{
							$ntime=mktime(0,0,0,intval(date('m',$from)),intval($test2)+1,intval(date('Y',$from)));
							if ($ntime<$period_end)
								$happenings[]=array($e_id,$event,$ntime,$to,$real_from,$real_to,$utc_real_from);
						}
					}
				}
			}

			if ($entries->is_empty())
			{
				$class='free_time';
				$text='&nbsp;';
				$entries=do_template('CALENDAR_MONTH_ENTRY_FREE',array('_GUID'=>'a5e193c8c14deb17ac629e0de74458a2','CLASS'=>$class,'TEXT'=>$text));
			}

			$day_url=build_url(array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$explode[0].'-'.$explode[1].'-'.str_pad(strval($i),2,'0',STR_PAD_LEFT)),'_SELF');
			$days->attach(do_template('CALENDAR_MONTH_DAY',array('_GUID'=>'44162c09e0647888cf079c0ac78c1912','CURRENT'=>date('Y-m-d')==date('Y-m-d',$timestamp),'DAY_URL'=>$day_url,'CLASS'=>$class,'DAY'=>strval($i),'ENTRIES'=>$entries)));
		}
		$ex_array=array_flip(array_reverse(array_flip($ex_array)));
		for ($x=0;$x<$ex_array[$day_of_week];$x++)
			$days->attach(do_template('CALENDAR_MONTH_DAY',array('_GUID'=>'3a34ca72f31d9244b9eb642814836736','CURRENT'=>false,'DAY_URL'=>'','CLASS'=>'','DAY'=>'','ENTRIES'=>$empty_entry)));
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'week','id'=>$explode[0].'-'.strval($week_count)));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$week_url=build_url($map,'_SELF');
		$weeks->attach(do_template('CALENDAR_MONTH_WEEK',array('_GUID'=>'0fc7a1691b3cb081d80519a2fe666849','WEEK_URL'=>$week_url,'WEEK_DATE'=>$week_date,'DAYS'=>$days)));

		return do_template('CALENDAR_MONTH',array('_GUID'=>'2ada774f9eb9524db1bed9bea440e5d1','WEEKS'=>$weeks,'PERIOD_START'=>strval($period_start),'PERIOD_END'=>strval($period_end)));
	}

	/**
	 * The calendar area view for viewing a single year.
	 *
	 * @param  string			The year we are viewing
	 * @param  string			The day (Y-m-d) we are viewing
	 * @param  array			List of components of our viewed ID
	 * @param  MEMBER			The member ID we are viewing as
	 * @param  ?array			The type filter (NULL: none)
	 * @return tempcode		The UI
	 */
	function view_calendar_view_year($view_id,$day,$explode,$member_id,$filter)
	{
		$period_start=mktime(0,0,0,1,1,intval($explode[0]));
		$period_end=mktime(0,0,0,1,0,intval($explode[0])+1);

		$happenings=calendar_matches($member_id,true,$period_start,$period_end,$filter);

		global $M_SORT_KEY;
		$M_SORT_KEY=0;
		usort($happenings,'multi_sort');

		$months='';
		$month_rows=new ocp_tempcode();
		for ($i=1;$i<=12;$i++)
		{
			$entries=array();
			$priorities=array();

			if ((($i-1)%3==0) && ($i!=1))
			{
				if ($i==4) list($month_a,$month_b,$month_c)=array(do_lang_tempcode('JANUARY'),do_lang_tempcode('FEBRUARY'),do_lang_tempcode('MARCH'));
				if ($i==7) list($month_a,$month_b,$month_c)=array(do_lang_tempcode('APRIL'),do_lang_tempcode('MAY'),do_lang_tempcode('JUNE'));
				if ($i==10) list($month_a,$month_b,$month_c)=array(do_lang_tempcode('JULY'),do_lang_tempcode('AUGUST'),do_lang_tempcode('SEPTEMBER'));
				$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$explode[0].'-'.strval($i-3)));
				if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
				$month_a_url=build_url($map,'_SELF');
				$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$explode[0].'-'.strval($i-2)));
				if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
				$month_b_url=build_url($map,'_SELF');
				$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$explode[0].'-'.strval($i-1)));
				if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
				$month_c_url=build_url($map,'_SELF');
				$month_rows->attach(do_template('CALENDAR_YEAR_MONTH_ROW',array('_GUID'=>'cf4f7b66a32afee1c69b2e4338af8dd5','MONTHS'=>$months,'MONTH_A_URL'=>$month_a_url,'MONTH_B_URL'=>$month_b_url,'MONTH_C_URL'=>$month_c_url,'MONTH_A'=>$month_a,'MONTH_B'=>$month_b,'MONTH_C'=>$month_c)));
				$months='';
			}

			foreach ($happenings as $happening)
			{
				list($e_id,$event,$from,$to,$real_from,$real_to,$utc_real_from)=$happening;
				$date=date('m',$from);
				if (intval($date)==$i)
				{
					if (is_numeric($e_id))
					{
						$map=array_merge($filter,array('page'=>'_SELF','type'=>'view','id'=>$event['e_id'],'day'=>date('Y-m-d',$utc_real_from),'date'=>$view_id,'back'=>'year'));
						if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
						$url=build_url($map,'_SELF');
					} else
					{
						$url=$e_id;
					}
					$icon=$event['t_logo'];
					$title=is_integer($event['e_title'])?get_translated_text($event['e_title']):$event['e_title'];
					$date=locale_filter(date(do_lang('calendar_date'),$real_from));
					$_from=$from;
					do
					{
						$_day=intval(date('d',$_from));

						if (!array_key_exists($_day,$entries))
						{
							$entries[$_day]=array('ID'=>strval($event['e_id']),'T_TITLE'=>array_key_exists('t_title',$event)?(is_string($event['t_title'])?$event['t_title']:get_translated_text($event['t_title'])):'RSS','PRIORITY'=>strval($event['e_priority']),'ICON'=>$icon,'TIME'=>$date,'TITLE'=>$title,'URL'=>$url);
							$priorities[$_day]=$event['e_priority'];
						} else // If we have more than one, we don't store a map, we just count them
						{
							$entries[$_day]=(is_array($entries[$_day]))?2:($entries[$_day]+1);
							$priorities[$_day]=min($priorities[$_day],$event['e_priority']);
						}

						// Maybe spanning multiple days of month
						if (!is_null($to))
						{
							$_explode=explode('-',date('d-m',$to));
							$continues=(intval($_explode[0])!=$_day) || (intval($_explode[1])!=$i);
							if ($continues)
							{
								$_from=mktime(0,0,0,intval(date('m',$_from)),intval(date('d',$_from))+1,intval(date('Y',$_from)));
								if (intval(date('m',$_from))!=$i) $continues=false; // Don't let it roll to another month
							}
						} else $continues=false;
					}
					while ($continues);

					// Push to other months too
					if (!is_null($to))
					{
						$date2=date('m',$to);
						if (intval($date2)>$i)
						{
							$ntime=mktime(0,0,0,intval(date('m',$from))+1,1,intval(date('Y',$from)));
							$happenings[]=array($e_id,$event,$ntime,$to,$real_from,$real_to,$utc_real_from);
						}
					}
				}
			}

			$_period_start=mktime(0,0,0,$i,1,intval($explode[0]));
			$_period_end=mktime(0,0,0,$i+1,0,intval($explode[0]));
			$_days=intval(round(floatval($_period_end-$_period_start)/floatval(60*60*24)));

			$_entries=new ocp_tempcode();
			$__entries=new ocp_tempcode();
			$_dotw=date('D',$_period_start);
			if (get_option('ssw')=='0')
			{
				$ex_array=array('Mon'=>0,'Tue'=>1,'Wed'=>2,'Thu'=>3,'Fri'=>4,'Sat'=>5,'Sun'=>6);
			} else
			{
				$ex_array=array('Sun'=>0,'Mon'=>1,'Tue'=>2,'Wed'=>3,'Thu'=>4,'Fri'=>5,'Sat'=>6);
			}
			$dotw=$ex_array[$_dotw];
			for ($j=0;$j<$dotw;$j++)
				$__entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_SPACER'));
			for ($j=1;$j<=$_days+1;$j++)
			{
				$date=$explode[0].'-'.str_pad(strval($i),2,'0',STR_PAD_LEFT).'-'.str_pad(strval($j),2,'0',STR_PAD_LEFT);
				$date_formatted=locale_filter(date(do_lang('calendar_date'),mktime(0,0,0,$i,$j,intval($explode[0]))));
				$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'day','id'=>$date));
				if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
				$day_url=build_url($map,'_SELF');

				if (!array_key_exists($j,$entries))
				{
					$class='free_time';
					$__entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_FREE',array('_GUID'=>'cccf839895121ae14fd1948ed4024d4b','CURRENT'=>date('Y-m-d')==$date,'DAY_URL'=>$day_url,'DATE'=>$date_formatted,'DAY'=>strval($j),'CLASS'=>$class)));
				}
				elseif (is_array($entries[$j]))
				{
					$class='single';
					$events_and_priority_lang=do_lang_tempcode('TOTAL_EVENTS_AND_HIGHEST_PRIORITY','1',do_lang_tempcode('PRIORITY_'.strval($priorities[$j])));
					$__entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_ACTIVE',array_merge(array('CURRENT'=>date('Y-m-d')==$date,'DAY_URL'=>$day_url,'DATE'=>$date_formatted,'DAY'=>strval($j),'CLASS'=>$class,'COUNT'=>'1','EVENTS_AND_PRIORITY_LANG'=>$events_and_priority_lang),$entries[$j])));
				}
				else
				{
					$class='multiple';
					$e_count=integer_format($entries[$j]);
					$events_and_priority_lang=do_lang_tempcode('TOTAL_EVENTS_AND_HIGHEST_PRIORITY',make_string_tempcode($e_count),do_lang_tempcode('PRIORITY_'.strval($priorities[$j])));
					$__entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_ACTIVE',array('_GUID'=>'9f016773fce6402eca1a7a0afa6bb89f','CURRENT'=>date('Y-m-d')==$date,'DAY_URL'=>$day_url,'DATE'=>$date_formatted,'TITLE'=>'','TIME'=>'','URL'=>'','ID'=>'','PRIORITY'=>strval($priorities[$j]),'DAY'=>strval($j),'ICON'=>'','COUNT'=>$e_count,'EVENTS_AND_PRIORITY_LANG'=>$events_and_priority_lang)));
				}

				if ($dotw==6)
				{
					$_entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_ROW',array('_GUID'=>'f0d29f0cf53ba119a7ab4351807a9f2f','ENTRIES'=>$__entries)));
					$__entries=new ocp_tempcode();
					$dotw=0;
				} else $dotw++;
			}

			if (!$__entries->is_empty())
			{
				for ($j=$dotw;$j<7;$j++)
					$__entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_SPACER'));
				$_entries->attach(do_template('CALENDAR_YEAR_MONTH_DAY_ROW',array('_GUID'=>'5c7fa5d50e1e9eb30f0a19cc7da03c4b','ENTRIES'=>$__entries)));
			}

			$month=do_template('CALENDAR_YEAR_MONTH',array('_GUID'=>'58c9f4cc04186dce6e7ea3dd8ec9269b','ENTRIES'=>$_entries));
			$months.=$month->evaluate()/*FUDGEFUDGE*/;
		}

		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$explode[0].'-'.strval($i-3)));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$month_a_url=build_url($map,'_SELF');
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$explode[0].'-'.strval($i-2)));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$month_b_url=build_url($map,'_SELF');
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>'month','id'=>$explode[0].'-'.strval($i-1)));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$month_c_url=build_url($map,'_SELF');
		list($month_a,$month_b,$month_c)=array(do_lang_tempcode('OCTOBER'),do_lang_tempcode('NOVEMBER'),do_lang_tempcode('DECEMBER'));
		$month_rows->attach(do_template('CALENDAR_YEAR_MONTH_ROW',array('_GUID'=>'d169c06319ca2c089faa6fbb92b39115','MONTHS'=>$months,'MONTH_A_URL'=>$month_a_url,'MONTH_B_URL'=>$month_b_url,'MONTH_C_URL'=>$month_c_url,'MONTH_A'=>$month_a,'MONTH_B'=>$month_b,'MONTH_C'=>$month_c)));

		return do_template('CALENDAR_YEAR',array('_GUID'=>'ee8f09d406eff18637b37b762bad62cd','MONTH_ROWS'=>$month_rows,'PERIOD_START'=>strval($period_start),'PERIOD_END'=>strval($period_end)));
	}

	/**
	 * View an event.
	 *
	 * @return tempcode		The UI
	 */
	function view_event()
	{
		check_specific_permission('view_calendar');

		global $NON_CANONICAL_PARAMS;
		$NON_CANONICAL_PARAMS[]='back';

		$id=get_param_integer('id');
		$filter=$this->get_filter();
		$GLOBALS['FEED_URL']=find_script('backend').'?mode=calendar&filter='.implode(',',$this->get_and_filter());

		$rows=$GLOBALS['SITE_DB']->query_select('calendar_events e LEFT JOIN '.$GLOBALS['SITE_DB']->get_table_prefix().'calendar_types t ON t.id=e.e_type',array('*'),array('e.id'=>$id),'',1);
		if (!array_key_exists(0,$rows))
		{
			warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
		}
		$event=$rows[0];
		if ($event['e_is_public']==0) enforce_personal_access($event['e_submitter'],'view_personal_events');

		if (!has_category_access(get_member(),'calendar',strval($event['e_type']))) access_denied('CATEGORY_ACCESS');

		// Validation
		$warning_details=new ocp_tempcode();
		if (($event['validated']==0) && ($event['e_is_public']==1))
		{
			if (!has_specific_permission(get_member(),'jump_to_unvalidated'))
				access_denied('SPECIFIC_PERMISSION','jump_to_unvalidated');

			$warning_details->attach(do_template('WARNING_TABLE',array('_GUID'=>'332faacba974e648a67e5e91ffd3d8e5','WARNING'=>do_lang_tempcode((get_param_integer('redirected',0)==1)?'UNVALIDATED_TEXT_NON_DIRECT':'UNVALIDATED_TEXT'))));
		}

		if (addon_installed('awards'))
		{
			require_code('awards');
			$awards=find_awards_for('event',strval($id));
		} else $awards=array();

		$_title=get_translated_text($event['e_title']);
		$title_to_use=do_lang_tempcode('CALENDAR_EVENT_VCAL',escape_html($_title));
		$title_to_use_2=do_lang('CALENDAR_EVENT',$_title);
		$title=get_page_title($title_to_use,false,NULL,NULL,$awards);

		seo_meta_load_for('event',strval($id),$title_to_use_2);

		$content=($event['e_type']==db_get_first_id())?make_string_tempcode(get_translated_text($event['e_content'])):get_translated_tempcode($event['e_content']);
		$type=get_translated_text($event['t_title']);
		$subscribed=new ocp_tempcode();
		if ((has_specific_permission(get_member(),'view_event_subscriptions')) && (cron_installed()))
		{
			$subscriptions=$GLOBALS['SITE_DB']->query_select('calendar_reminders',array('DISTINCT n_member_id'),array('e_id'=>$id),'',100);
			if (count($subscriptions)<100)
			{
				foreach ($subscriptions as $subscription)
				{
					$username=$GLOBALS['FORUM_DRIVER']->get_username($subscription['n_member_id']);
					if (!is_null($username))
					{
						$member_url=$GLOBALS['FORUM_DRIVER']->member_profile_url($subscription['n_member_id'],false,true);
						$subscribed->attach(do_template('CALENDAR_SCREEN_SUBSCRIPTION',array('_GUID'=>'c756b8b3f0c57494fd46a94e9abce029','MEMBER_URL'=>$member_url,'USERNAME'=>$username)));
					}
				}
			}
		}

		$__first_date=mktime($event['e_start_hour'],$event['e_start_minute'],0,$event['e_start_month'],$event['e_start_day'],$event['e_start_year']);
		$_first_date=cal_utctime_to_usertime(
			$__first_date,
			$event['e_timezone'],
			$event['e_do_timezone_conv']==1
		);
		$first_date=date('Y-m-d',$_first_date);
		$date=get_param('date',$first_date); // It's year 10,000 compliant when it comes to year display ;).
		$back_type=get_param('back','day');
		$map=array_merge($filter,array('page'=>'_SELF','type'=>'misc','view'=>$back_type,'id'=>$date));
		if (get_param_integer('member_id',get_member())!=get_member()) $map['member_id']=get_param_integer('member_id');
		$back_url=build_url($map,'_SELF');

		list($rating_details,$comment_details,$trackback_details)=embed_feedback_systems(
			'events',
			strval($id).(($event['e_seg_recurrences']==1)?('_'.$date):''),
			$event['allow_rating'],
			$event['allow_comments'],
			$event['allow_trackbacks'],
			((get_option('is_on_strong_forum_tie')=='0') || ($event['e_is_public']==1))?1:0,
			$event['e_submitter'],
			build_url(array('page'=>'_SELF','type'=>'entry','id'=>$id),'_SELF',NULL,false,false,true),
			$_title,
			get_value('comment_forum__calendar')
		);

		$_subscriptions=new ocp_tempcode();
		if ((is_guest()) || (!cron_installed()))
		{
			$subscribe_url=new ocp_tempcode();
		} else
		{
			$subscriptions=$GLOBALS['SITE_DB']->query_select('calendar_reminders',array('id','n_seconds_before'),array('e_id'=>$id,'n_member_id'=>get_member()));
			foreach ($subscriptions as $subscription)
			{
				$time=display_time_period(intval($subscription['n_seconds_before']));
				$unsubscribe_url=build_url(array('page'=>'_SELF','type'=>'unsubscribe_event','id'=>$id,'reminder_id'=>$subscription['id']),'_SELF');
				$_subscriptions->attach(do_template('CALENDAR_SCREEN_PERSONAL_SUBSCRIPTION',array('_GUID'=>'cc36c75ea516abfab1c65328c0e290fc','UNSUBSCRIBE_URL'=>$unsubscribe_url,'TIME'=>$time)));
			}
			$subscribe_url=build_url(array('page'=>'_SELF','type'=>'subscribe_event','id'=>$id),'_SELF');
		}

		if ((has_actual_page_access(NULL,'cms_calendar',NULL,NULL)) && (has_edit_permission(($event['e_is_public']==0)?'low':'mid',get_member(),$event['e_submitter'],'cms_calendar',array('calendar',$event['e_type']))))
		{
			$edit_url=build_url(array('page'=>'cms_calendar','type'=>'_ed','id'=>$id),get_module_zone('cms_calendar'));
		} else
		{
			$edit_url=new ocp_tempcode();
		}

		if ($event['e_seg_recurrences']==0)
		{
			$NON_CANONICAL_PARAMS[]='day';
			$NON_CANONICAL_PARAMS[]='date';
		}

		$day=get_param('day','');
		if ($day!='')
		{
			$explode=explode('-',$day);
			if (count($explode)==3)
			{
				if (is_null($event['e_start_hour'])) // All day event
				{
					if (is_null($event['e_end_year']) || is_null($event['e_end_month']) || is_null($event['e_end_day']))
					{
						$event['e_end_day']=$event['e_start_day'];
						$event['e_end_month']=$event['e_start_month'];
						$event['e_end_year']=$event['e_start_year'];
					}
				}
				if (!is_null($event['e_end_year']) && !is_null($event['e_end_month']) && !is_null($event['e_end_day']))
				{
					$event['e_end_year']+=intval($explode[0])-$event['e_start_year'];
					$event['e_end_month']+=intval($explode[1])-$event['e_start_month'];
					$event['e_end_day']+=intval($explode[2])-$event['e_start_day'];
				}
				$event['e_start_year']=intval($explode[0]);
				$event['e_start_month']=intval($explode[1]);
				$event['e_start_day']=intval($explode[2]);
			}
		}

		$time_raw=cal_get_start_utctime_for_event($event['e_timezone'],$event['e_start_year'],$event['e_start_month'],$event['e_start_day'],$event['e_start_hour'],$event['e_start_minute'],$event['e_do_timezone_conv']==1);
		$from=cal_utctime_to_usertime($time_raw,$event['e_timezone'],$event['e_do_timezone_conv']==1);
		$day_formatted=locale_filter(date(do_lang('calendar_date'),$from));
		if (!is_null($event['e_end_year']) && !is_null($event['e_end_month']) && !is_null($event['e_end_day']))
		{
			$to_raw=cal_get_end_utctime_for_event($event['e_timezone'],$event['e_end_year'],$event['e_end_month'],$event['e_end_day'],$event['e_end_hour'],$event['e_end_minute'],$event['e_do_timezone_conv']==1);
			$to=cal_utctime_to_usertime($to_raw,$event['e_timezone'],$event['e_do_timezone_conv']==1);
			$to_day_formatted=locale_filter(date(do_lang('calendar_date'),$to));
			$time2=date_range($from,$to,!is_null($event['e_start_hour']));
		} else
		{
			$to_raw=NULL;
			$time2=is_null($event['e_start_hour'])?'':locale_filter(my_strftime(do_lang('calendar_minute'),$from));
			$to=NULL;
			$to_day_formatted=NULL;
		}

		$priority=$event['e_priority'];
		$is_public=($event['e_is_public']==1)?do_lang_tempcode('YES'):do_lang_tempcode('NO');

		$recurrence=do_lang_tempcode('NA_EM');
		if (($event['e_recurrence']!='none') && ($event['e_recurrence']!=''))
		{
			$l_code=explode(' ',strtoupper($event['e_recurrence']));
			$recurrence=do_lang_tempcode($l_code[0]);

			if (!is_null($event['e_recurrences']))
			{
				$recurrence=do_lang_tempcode('RECURRENCE_ITERATION',$recurrence,make_string_tempcode(integer_format($event['e_recurrences'])));
			}
		} elseif ($day=='') $day=$first_date;

		$priority_lang=do_lang_tempcode('PRIORITY_'.strval($priority));

		// Views
		if (get_db_type()!='xml')
		{
			$event['e_views']++;
			$GLOBALS['SITE_DB']->query_update('calendar_events',array('e_views'=>$event['e_views']),array('id'=>$id),'',1,NULL,false,true);
		}

		breadcrumb_set_parents(array(array($back_url,do_lang_tempcode('CALENDAR'))));

		$GLOBALS['META_DATA']+=array(
			'created'=>date('Y-m-d',$event['e_add_date']),
			'creator'=>$GLOBALS['FORUM_DRIVER']->get_username($event['e_submitter']),
			'publisher'=>'', // blank means same as creator
			'modified'=>is_null($event['e_edit_date'])?'':date('Y-m-d',$event['e_edit_date']),
			'type'=>'Calendar event',
			'title'=>get_translated_text($event['e_title']),
			'identifier'=>'_SEARCH:calendar:view:'.strval($id),
			'description'=>get_translated_text($event['e_content']),
			'image'=>find_theme_image('bigicons/calendar'),
		);

		$map=array(
			'TITLE'=>$title,
			'_TITLE'=>get_translated_text($event['e_title']),
			'ID'=>strval($id),
			'TAGS'=>get_loaded_tags('calendar'),
			'WARNING_DETAILS'=>$warning_details,
			'SUBMITTER'=>strval($event['e_submitter']),
			'ADD_DATE'=>get_timezoned_date($event['e_add_date']),
			'ADD_DATE_RAW'=>strval($event['e_add_date']),
			'EDIT_DATE_RAW'=>is_null($event['e_edit_date'])?'':strval($event['e_edit_date']),
			'VIEWS'=>integer_format($event['e_views']),
			'LOGO'=>$event['t_logo'],
			'DAY'=>$day_formatted,
			'TO_DAY'=>$to_day_formatted,
			'RECURRENCE'=>$recurrence,
			'IS_PUBLIC'=>$is_public,
			'PRIORITY'=>strval($priority),
			'PRIORITY_LANG'=>$priority_lang,
			'TYPE'=>$type,
			'TIME'=>$time2,
			'TIME_RAW'=>strval($time_raw),
			'TIME_VCAL'=>date('Ymd',$time_raw)."T".date('His',$time_raw),
			'TO_TIME_VCAL'=>is_null($to_raw)?NULL:(date('Ymd',$to_raw)."T".date('His',$to_raw)),
			'EDIT_URL'=>$edit_url,
			'SUBSCRIPTIONS'=>$_subscriptions,
			'SUBSCRIBE_URL'=>$subscribe_url,
			'BACK_URL'=>$back_url,
			'CONTENT'=>$content,
			'SUBSCRIBED'=>$subscribed,
			'RATING_DETAILS'=>$rating_details,
			'TRACKBACK_DETAILS'=>$trackback_details,
			'COMMENT_DETAILS'=>$comment_details,
			'_GUID'=>'602e6f86f586ef0a24efed950eafd426',
		);

		if ($event['e_do_timezone_conv']==0)
		{
			$timezone_map=get_timezone_list();
			$map['TIMEZONE']=$timezone_map[$event['e_timezone']];
		}

		return do_template('CALENDAR_SCREEN',$map);
	}

	/**
	 * Interface to subscribe for reminders to an event.
	 *
	 * @return tempcode		The UI
	 */
	function subscribe_event()
	{
		$title=get_page_title('SUBSCRIBE_EVENT');

		breadcrumb_set_parents(array(array('_SELF:_SELF:misc',do_lang_tempcode('CALENDAR'))));

		// Check access
		$id=get_param_integer('id');
		check_specific_permission('view_calendar');
		$rows=$GLOBALS['SITE_DB']->query_select('calendar_events',array('*'),array('id'=>$id),'',1);
		if (!array_key_exists(0,$rows))
		{
			warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
		}
		$event=$rows[0];
		if ($event['e_is_public']==0) enforce_personal_access($event['e_submitter'],'view_personal_events');

		if (!has_category_access(get_member(),'calendar',strval($event['e_type']))) access_denied('CATEGORY_ACCESS');

		require_code('form_templates');

		$post_url=build_url(array('page'=>'_SELF','type'=>'_subscribe_event','id'=>$id),'_SELF');
		$fields=new ocp_tempcode();
		$fields->attach(form_input_float(do_lang_tempcode('REMINDER_TIME'),do_lang_tempcode('DESCRIPTION_REMINDER_TIME'),'hours_before',1.0,true));
		$submit_name=do_lang_tempcode('SUBSCRIBE_EVENT');

		return do_template('FORM_SCREEN',array('_GUID'=>'8f6a962617031264ee1af552701804ca','SKIP_VALIDATION'=>true,'HIDDEN'=>'','TITLE'=>$title,'TEXT'=>'','FIELDS'=>$fields,'URL'=>$post_url,'SUBMIT_NAME'=>$submit_name));
	}

	/**
	 * Subscribe for reminders to an event.
	 *
	 * @return tempcode		The UI
	 */
	function _subscribe_event()
	{
		$title=get_page_title('SUBSCRIBE_EVENT');

		$seconds_before=intval(floatval(post_param('hours_before'))*3600.0);

		$id=get_param_integer('id'); // The event ID
		$events=$GLOBALS['SITE_DB']->query_select('calendar_events',array('*'),array('id'=>get_param_integer('id')),'',1);
		$event=$events[0];

		$rem_id=$GLOBALS['SITE_DB']->query_insert('calendar_reminders',array(
			'e_id'=>$id,
			'n_member_id'=>get_member(),
			'n_seconds_before'=>$seconds_before
		),true);

		if ((has_actual_page_access($GLOBALS['FORUM_DRIVER']->get_guest_id(),'calendar')) && (has_category_access($GLOBALS['FORUM_DRIVER']->get_guest_id(),'calendar',strval($event['e_type']))))
		{
			$_from=cal_get_start_utctime_for_event($event['e_timezone'],$event['e_start_year'],$event['e_start_month'],$event['e_start_day'],$event['e_start_hour'],$event['e_start_minute'],$event['e_do_timezone_conv']==1);
			$from=cal_utctime_to_usertime($_from,$event['e_timezone'],$event['e_do_timezone_conv']==1);
			$to=mixed();
			if (!is_null($event['e_end_year']) && !is_null($event['e_end_month']) && !is_null($event['e_end_day']))
			{
				$_to=cal_get_end_utctime_for_event($event['e_timezone'],$event['e_end_year'],$event['e_end_month'],$event['e_end_day'],$event['e_end_hour'],$event['e_end_minute'],$event['e_do_timezone_conv']==1);
				$to=cal_utctime_to_usertime($_to,$event['e_timezone'],$event['e_do_timezone_conv']==1);
			}

			syndicate_described_activity('calendar:ACTIVITY_SUBSCRIBED_EVENT',get_translated_text($event['e_title']),date_range($from,$to,!is_null($event['e_start_hour'])),'','_SEARCH:calendar:view:'.strval($id),'','','calendar',1,NULL,true);
		}

		// Add next reminder to job system
		$recurrences=find_periods_recurrence($event['e_timezone'],1,$event['e_start_year'],$event['e_start_month'],$event['e_start_day'],is_null($event['e_start_hour'])?0:$event['e_start_hour'],is_null($event['e_start_minute'])?0:$event['e_start_minute'],$event['e_end_year'],$event['e_end_month'],$event['e_end_day'],is_null($event['e_end_hour'])?0:$event['e_end_hour'],is_null($event['e_end_minute'])?0:$event['e_end_minute'],$event['e_recurrence'],min(1,$event['e_recurrences']));
		if (array_key_exists(0,$recurrences))
		{
			$GLOBALS['SITE_DB']->query_insert('calendar_jobs',array(
				'j_time'=>usertime_to_utctime($recurrences[0][0])-$seconds_before,
				'j_reminder_id'=>$rem_id,
				'j_member_id'=>get_member(),
				'j_event_id'=>get_param_integer('id')
			));
		}

		$url=build_url(array('page'=>'_SELF','type'=>'view','id'=>get_param_integer('id')),'_SELF');
		return redirect_screen($title,$url,do_lang_tempcode('SUCCESS'));
	}

	/**
	 * Unsubscribe for reminders to an event.
	 *
	 * @return tempcode		The UI
	 */
	function unsubscribe_event()
	{
		$title=get_page_title('UNSUBSCRIBE_EVENT');

		$GLOBALS['SITE_DB']->query_delete('calendar_reminders',array('id'=>get_param_integer('reminder_id')),'',1); // reminder_id being the reminder ID

		$GLOBALS['SITE_DB']->query_delete('calendar_jobs',array(
			'j_reminder_id'=>get_param_integer('reminder_id'),
			'j_member_id'=>get_member()
		));

		$url=build_url(array('page'=>'_SELF','type'=>'view','id'=>get_param_integer('id')),'_SELF'); // id being the event ID
		return redirect_screen($title,$url,do_lang_tempcode('SUCCESS'));
	}

	/**
	 * Declare interests for event types.
	 *
	 * @return tempcode		The UI
	 */
	function interests()
	{
		$types=$GLOBALS['SITE_DB']->query_select('calendar_types',array('id'));

		$GLOBALS['SITE_DB']->query_delete('calendar_interests',array('i_member_id'=>get_member()));
		foreach ($types as $type)
		{
			$status=(post_param_integer('int_'.strval($type['id']),0)==1 || (post_param_integer('id',NULL)===$type['id']));
			if ($status)
			{
				$GLOBALS['SITE_DB']->query_insert('calendar_interests',array('i_member_id'=>get_member(),'t_type'=>$type['id']));
			}
		}

		$url=build_url(array('page'=>'_SELF','type'=>'misc','view'=>get_param('view'),'id'=>get_param('id')),'_SELF'); // id being the event ID
		$title=get_page_title('DECLARE_EVENT_INTEREST');
		return redirect_screen($title,$url,do_lang_tempcode('SUCCESS'));
	}

	/**
	 * Declare interest to an event type.
	 *
	 * @return tempcode		The UI
	 */
	function declare_interest()
	{
		$title=get_page_title('DECLARE_EVENT_INTEREST');

		$GLOBALS['SITE_DB']->query_insert('calendar_interests',array('i_member_id'=>get_member(),'t_type'=>get_param_integer('t_type')));

		$url=build_url(array('page'=>'_SELF','type'=>'misc','view'=>get_param('view'),'id'=>get_param('id')),'_SELF');
		return redirect_screen($title,$url,do_lang_tempcode('SUCCESS'));
	}

	/**
	 * Undeclare interest to an event type.
	 *
	 * @return tempcode		The UI
	 */
	function undeclare_interest()
	{
		$title=get_page_title('UNDECLARE_EVENT_INTEREST');

		$GLOBALS['SITE_DB']->query_delete('calendar_interests',array('i_member_id'=>get_member(),'t_type'=>get_param('t_type')),'',1);

		$url=build_url(array('page'=>'_SELF','type'=>'misc','view'=>get_param('view'),'id'=>get_param('id')),'_SELF');
		return redirect_screen($title,$url,do_lang_tempcode('SUCCESS'));
	}

}


