############################################################## 
## MOD Title: Birthdays
## MOD Author: TerraFrost < terrafrost@phpbb.com > (Jim Wigginton) http://www.frostjedi.com/terra/wordpress/
## MOD Description: Adds a Birthday field to the user profile.
## MOD Version: 2.0.2
##
## Installation Level: moderate
## Installation Time: 30 Minutes
##
## Files To Edit: 15
##      language/lang_english/lang_main.php
##      language/lang_english/lang_admin.php
##      includes/functions_selects.php
##      includes/template.php
##      includes/usercp_register.php
##      includes/usercp_avatar.php
##      templates/subSilver/profile_add_body.tpl
##      admin/admin_users.php
##      templates/subSilver/admin/user_edit_body.tpl
##      index.php
##      templates/subSilver/index_body.tpl
##      includes/usercp_viewprofile.php
##      templates/subSilver/profile_view_body.tpl
##      admin/admin_board.php
##      templates/subSilver/admin/board_config_body.tpl
##
## Included Files: 2
##      templates/subSilver/images/icon_birthday.gif
##      templates/subSilver/birthday_interface.tpl
## License: http://opensource.org/licenses/gpl-license.php GNU General Public License v2
##############################################################
## For security purposes, please check: http://www.phpbb.com/mods/
## for the latest version of this MOD. Although MODs are checked
## before being allowed in the MODs Database there is no guarantee
## that there are no security problems within the MOD. No support
## will be given for MODs not found within the MODs Database which
## can be found at http://www.phpbb.com/mods/
##############################################################
## Author Notes:
##
##   Although Niels Chr. Denmark's ever-popular Birthday Hack already does that
##   which this MOD attempts to do, it does have a number of problems that this
##   MOD seeks to remedy.
##
##     1. It isn't validated and hasn't been subjected to a security audit by the
##        phpBB MOD Team.
##     2. It saves the birthdate by saving the number of seconds that have elapsed
##        since January 1, 1970 to the date in question.  This is an inefficient use
##        of memory and of storage.
##     3. As a consequence of (2), members *must* specify a birth year, thereby
##        effectively telling the world how old they are.  This will probably 
##        disuade some members from using the feature when they might have otherwise
##        done so.
##
##   Niels MOD can be found here:
##   http://www.phpbbhacks.com/download/187
##
##   The current Birthday Icon is from Ptirhiik's Profile Control Panel.  The full
##   set of PSDs can be found here (doing a search for "graphics" will yield the exact
##   location):
##   http://rpgnet.clanmckeen.com/demo/download.php
##
##   Here's the direct link:
##   http://rpgnet.clanmckeen.com/mod/mod-profilcp/mod-profilcp%20-%20GIF_src.zip
##
##   The Birthday Icon in the contrib subdirectory is from David Vignoni's LGPL-Licensed
##   Nuvola Icon Theme for KDE 3.x.  The full set of PNGs can be found here (the exact
##   filename / location is nuvola/*/apps/cookie.png):
##   http://icon-king.com/?p=15
##
##   Here's a direct link to the individual PNG:
##   http://en.wikipedia.org/wiki/Image:Nuvola_apps_cookie.png
##
##   The latest version of this mod can be found here:
##   http://www.frostjedi.com/terra/scripts/phpbb/birthdays.zip
##
##   Some modifications to this MOD can be found here:
##   http://www.frostjedi.com/terra/scripts/phpbb/misc/birthdays_mods.zip
##
##   For support / comments / whatever, visit the following URL:
##   http://www.phpbb.com/phpBB/viewtopic.php?t=342028
##
##############################################################
## MOD History: 
##
##   2006-09-21 - Version 2.0.2
##      - rewrote the birthdate validation stuff (fixes a bug found by ycl6 - thanks!)
##      - the user_birthday column in phpbb_users is now defined as an index (thanks, eoinoc333!)
##      - added the ability to use drop-down menus for the year
##      - fixed a bug whereby birthday_interface occasionally duplicated things (thanks, Baltazar!)
##      - birthday_interface should now work better with caching template.php's (thanks, Jhong!)
##      - only active users are shown in index.php's birthday panel (thanks, RMcGirr83!)
##      - users in index.php's birthday panel are now sorted (thanks, saaiberke!)
##   2006-04-20 - Version 2.0.1
##      - revamped the documentation (Acyd Burn's Attachment MOD documentation inspired me)
##      - the birthday interface now displays in a manner dependant upon $lang['DATE_FORMAT'].
##      - 'yyyy' no longer needs to be inputted to reset birthdays.
##      - fixed a bug whereby dates would show up incorrectly (thanks, Eddy Kiprich!)
##   2005-12-30 - Version 2.0.0
##      - added a new birthday icon (thanks, Ptirhiik!)
##      - added the ability to require dates of birth
##      - added the ability to require years
##      - added the ability to make dates of birth "read-only"
##      - added the ability to make the birthday panel on the main index appear all the time
##      - added the ability to display birthdays that are x days away (where x is definable
##           in the ACP)
##      - added the ability to specify valid age ranges
##   2005-12-05 - Version 1.0.1
##      - added a new birthday icon (thanks, cback!)
##      - removed a superfluous rowspan="2" attribute (thanks, tony44!)
##      - fixed a bug whereby certain birthdates wouldn't be accepted in the ACP (thanks, duena!)
##      - fixed a bug whereby birthdates showed up a day early (thanks, 3Di and Scorpiuscat1!)
##      - fixed a bug whereby avatar galleries would reset birthdates (thanks, 3Di!)
##   2005-11-16 - Version 1.0.0
##      - initial release
##############################################################
## Before Adding This MOD To Your Forum, You Should Back Up All Files Related To This MOD 
##############################################################

#
#-----[ COPY ]------------------------------------------
#
copy templates/subSilver/images/icon_birthday.gif to templates/subSilver/images/icon_birthday.gif
copy templates/subSilver/birthday_interface.tpl to templates/subSilver/birthday_interface.tpl
#
#-----[ SQL ]-------------------------------------------
#
ALTER TABLE phpbb_users ADD user_birthday int(8) DEFAULT '0' NOT NULL;
ALTER TABLE phpbb_users ADD KEY(user_birthday);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_show',1);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_require',0);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_year',0);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_lock',0);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_lookahead',7);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_max',100);
INSERT INTO phpbb_config (config_name, config_value) VALUES ('bday_min',5);
#
#-----[ OPEN ]------------------------------------------
#
language/lang_english/lang_main.php
#
#-----[ FIND ]------------------------------------------
#
?>
#
#-----[ BEFORE, ADD ]-----------------------------------
#
$lang['Birthday_range'] = 'Birthdays must yield ages between %d and %d years, inclusive.';
$lang['No_birthdays'] = 'No birthdays today';
$lang['Congratulations'] = 'Congratulations to: <b>%s</b>';
$lang['Upcoming_birthdays'] = 'Users with a birthday within the next %d days: <b>%s</b>';
$lang['No_upcoming'] = 'No users are having a birthday in the upcoming %d days';
$lang['Birthday'] = 'Date of Birth';
$lang['Month'] = 'Month';
$lang['Day'] = 'Day';
$lang['Year'] = 'Year';
$lang['Clear'] = 'Clear';
$lang['Year_Optional'] = 'Year <i>(Optional)</i>';
$lang['Optional'] = '<i>(Optional)</i>';
$lang['Default_Month'] = '[ Select a Month ]';
$lang['Default_Day'] = 'dd';
$lang['Default_Year'] = 'yyyy';
$lang['Birthday_invalid'] = 'You didn\'t specify a valid Birthday.';
$lang['Todays_Birthdays'] = 'Today\'s Birthdays';
$lang['View_Birthdays'] = 'Happy Birthday!';

#
#-----[ OPEN ]------------------------------------------
#
language/lang_english/lang_admin.php
#
#-----[ FIND ]------------------------------------------
#
?>
#
#-----[ BEFORE, ADD ]-----------------------------------
#
$lang['Birthdays'] = 'Birthdays';
$lang['bday_show'] = 'Birthday Panel Visibility';
$lang['Unconditional'] = 'Unconditional';
$lang['Conditional'] = 'Conditional';
$lang['bday_show_explain'] = 'Determines whether or not the Birthday Panel on the main Index should be visible in the event that there are no birthdays or upcoming birthdays (unconditional = yes, conditional = no)';
$lang['bday_require'] = 'Require Date of Birth';
$lang['bday_require_explain'] = 'The year of birth will only be required if the "Require Year" option is selected';
$lang['bday_year'] = 'Require Year';
$lang['bday_year_explain'] = 'When this option is selected, users attempting to provide a date of birth will also need to provide a year of birth.';
$lang['bday_lock'] = 'Disallow Date of Birth Changes';
$lang['bday_lock_explain'] = 'Once entered, the date of birth cannot be changed, again.  Atleast when this option is selected.';
$lang['bday_lookahead'] = 'Number of Days to Look Ahead';
$lang['bday_lookahead_explain'] = 'Affects the Birthday Panel on the main Index.  Entering -1 will disable Birthday Lookahead';
$lang['bday_age_range'] = 'Allowable Age Range (in years)';

#
#-----[ OPEN ]------------------------------------------
#
includes/functions_selects.php
#
#-----[ FIND ]------------------------------------------
#
?>
#
#-----[ BEFORE, ADD ]-----------------------------------
#
//
// Pick a birthday month
//
function bday_month_select($default, $select_name = 'bday_month')
{
	global $lang;
	static $translate = array();

	if ( empty($translate) )
	{
		$translate = array(
			$lang['Default_Month'],
			$lang['datetime']['January'],
			$lang['datetime']['February'],
			$lang['datetime']['March'],
			$lang['datetime']['April'],
			$lang['datetime']['May'],
			$lang['datetime']['June'],
			$lang['datetime']['July'],
			$lang['datetime']['August'],
			$lang['datetime']['September'],
			$lang['datetime']['October'],
			$lang['datetime']['November'],
			$lang['datetime']['December']
		);
	}

	if ( !isset($default) )
	{
		$default = 0;
	}
	$bday_month_select = '<select name="' . $select_name . '">';

	foreach ($translate as $num => $month)
	{
		$selected = ( $num == $default ) ? ' selected="selected"' : '';
		$bday_month_select .= '<option value="' . $num . '"' . $selected . '>' . $month . '</option>';
	}
	$bday_month_select .= '</select>';

	return $bday_month_select;
}

//
// Pick a birthday day
//
function bday_day_select($default, $select_name = 'bday_day')
{
	global $lang;
	static $options;

	if ( empty($options) )
	{
		$options = array($lang['Default_Day']);
		for ($i=0; $i<31; $i++)
		{
			$options[] = $i + 1;
		}
	}

	if ( !isset($default) )
	{
		$default = 0;
	}
	$bday_day_select = '<select name="' . $select_name . '">';

	foreach ($options as $num => $day)
	{
		$selected = ( $num == $default ) ? ' selected="selected"' : '';
		$bday_day_select .= '<option value="' . $num . '"' . $selected . '>' . $day . '</option>';
	}
	$bday_day_select .= '</select>';

	return $bday_day_select;
}

//
// Pick a birthday year
//
function bday_year_select($default, $select_name = 'bday_year')
{
	global $board_config, $lang;

	if ( !isset($default) )
	{
		$default = 0;
	}
	$bday_year_select = '<select name="' . $select_name . '">';

	$end = gmdate('Y') - $board_config['bday_min'];
	$start = gmdate('Y') - $board_config['bday_max'] - 1;

	$selected = ( !$default ) ? ' selected="selected"' : '';
	$bday_year_select .= '<option value="0"' . $selected . '>' . $lang['Default_Year'] . '</option>';

	for ($i=$end;$i>=$start;$i--)
	{
		$selected = ( $i == $default ) ? ' selected="selected"' : '';
		$bday_year_select .= '<option value="' . $i . '"' . $selected . '>' . $i . '</option>';
	}
	$bday_year_select .= '</select>';

	return $bday_year_select;
}

#
# start editing the user profile files
#-----[ OPEN ]------------------------------------------
#
includes/template.php
#
#-----[ FIND ]------------------------------------------
# this is a partial match
#
	/**
	 * Generates a full path+filename for the given filename, which can either
	 * be an absolute name, or a name relative to the rootdir for this Template
	 * object.
	 */
	function make_filename($filename
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	function birthday_interface()
	{
		global $lang;

		// the following was adapted from bbcode.php's load_bbcode_template function.
		$bday_filename = $this->make_filename('birthday_interface.tpl');
		$fp = fopen($bday_filename, 'r');
		$temp = fread($fp, filesize($bday_filename));
		fclose($fp);

		$temp = str_replace('\\', '\\\\', $temp);
		$temp = str_replace('\'', '\\\'', $temp);
		$temp = str_replace("\n", '', $temp);
		$temp = preg_replace('#<!-- BEGIN (.*?) -->(.*?)<!-- END (.*?) -->#', "\n" . '$bday_tpls[\'\\1\'] = \'\\2\';', $temp);

		$bday_tpls = array();

		eval($temp);

		$bday_format = preg_replace('#\\\\.|[^djFmMnYy]#','',$lang['DATE_FORMAT']);
		$bday_format = substr(chunk_split($bday_format,1,'.'),0,-1);

		$bday_template = isset($bday_tpls['bday_start']) ? $bday_tpls['bday_start'] : '';

		$i = '';
		while ( isset($bday_tpls["bday_month$i"]) && isset($bday_tpls["bday_day$i"]) && isset($bday_tpls["bday_year$i"]) )
		{
			if ( !empty($i) )
			{
				$bday_template.= isset($bday_tpls['bday_glue']) ? $bday_tpls['bday_glue'] : '';
			}
			$bday_template.= strtr($bday_format,array(
				'd' => $bday_tpls["bday_day$i"],
				'j' => $bday_tpls["bday_day$i"],
				'F' => $bday_tpls["bday_month$i"],
				'm' => $bday_tpls["bday_month$i"],
				'M' => $bday_tpls["bday_month$i"],
				'n' => $bday_tpls["bday_month$i"],
				'Y' => $bday_tpls["bday_year$i"],
				'y' => $bday_tpls["bday_year$i"],
				'.' => isset($bday_tpls["bday_subglue$i"]) ? $bday_tpls["bday_subglue$i"] : '')
			);
			if ( empty($i) )
			{
				$i = 1;
			}
			$i++;
		}

		$bday_template.= isset($bday_tpls['bday_end']) ? $bday_tpls['bday_end'] : '';

		$this->uncompiled_code['bday_interface'] = trim($bday_template);

		// the following two lines are required for phpBB's that use the eXtreme Styles MOD, cache/template_file_cache.php,
		// or other files bearing some sort of semblance to either of those.  on phpBB's not using those MODs, these lines
		// don't do much of anything.

		// also, if you're using cache/template_file_cache.php and you change $lang['DATE_FORMAT'], you'll need to delete
		// the appropriate *.php file in the birthday_interface directory.

		$this->files['bday_interface'] = $this->make_filename('birthday_interface.tpl');
		$this->filename['bday_interface'] = 'birthday_interface_'.$board_config['default_lang'].'.tpl';

		$this->assign_var_from_handle('BIRTHDAY_INTERFACE','bday_interface');
	}

#
#-----[ OPEN ]------------------------------------------
#
includes/usercp_register.php
#
#-----[ FIND ]------------------------------------------
# this is a partial match
#
	$strip_var_list = array
#
#-----[ IN-LINE FIND ]----------------------------------
#
, 'icq' => 'icq'
#
#-----[ IN-LINE AFTER, ADD ]----------------------------
#
, 'bday_day' => 'bday_day', 'bday_month' => 'bday_month', 'bday_year' => 'bday_year'
#
#-----[ FIND ]------------------------------------------
#
	if ( $signature != '' )
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	$bday_locked = $board_config['bday_lock'] && $userdata['user_birthday'] != 0;

	if ( !$bday_locked )
	{
		$empty_month = empty($bday_month) || $bday_month == $lang['Default_Month'];
		$empty_day = empty($bday_day) || $bday_day == $lang['Default_Day'];
		$empty_year = empty($bday_year) || $bday_year == $lang['Default_Year'];
	}
	else
	{
		$empty_month = false;
		$empty_day = false;
		$empty_year = false;
		// we set the following to 1 since otherwise we'd be assigning undefined variables to $temp_*
		$bday_month = $bday_day = $bday_year = 1;
	}

	$temp_month = $empty_month ? 1 : $bday_month;
	$temp_day = $empty_day ? 1 : $bday_day;
	$temp_year = $empty_year ? 4 : $bday_year;

	switch (true)
	{
		case $board_config['bday_require'] && $board_config['bday_year'] && ($empty_month || $empty_day || $empty_year):
		case $board_config['bday_require'] && !$board_config['bday_year'] && ($empty_month || $empty_day):
		case !$board_config['bday_require'] && $board_config['bday_year'] && (($empty_month != $empty_day) || ($empty_day != $empty_year)):
		case !$board_config['bday_require'] && !$board_config['bday_year'] && (($empty_month != $empty_day) || ($empty_day && !$empty_year)):
		case !@checkdate( $temp_month, $temp_day, $temp_year ):
			$error = TRUE;
			$error_msg .= ( ( !empty($error_msg) ) ? '<br />' : '' ) . $lang['Birthday_invalid'];
			break;
		case !$empty_month && !$empty_day && !$empty_year && !$bday_locked:
			$age = gmdate('Y') - $bday_year - ( sprintf('%02d%02d',$bday_month,$bday_day) > gmdate('md',time()) );
			if ( $board_config['bday_min'] > $age || $age > $board_config['bday_max'] )
			{
				$error = TRUE;
				$error_msg .= ( ( !empty($error_msg) ) ? '<br />' : '' ) . sprintf($lang['Birthday_range'],$board_config['bday_min'],$board_config['bday_max']);
			}
	}

#
#-----[ FIND ]------------------------------------------
# this is a partial match
#
			$sql = "UPDATE " . USERS_TABLE . "
				SET " . $username_sql . $passwd_sql
#
#-----[ BEFORE, ADD ]-----------------------------------
#
			$user_birthday = ( !$board_config['bday_lock'] || $userdata['user_birthday'] == 0 ) ? sprintf('%02d%02d%04d',$bday_month,$bday_day,$bday_year) : $userdata['user_birthday'];
#
#-----[ IN-LINE FIND ]----------------------------------
#
, user_icq = '" . str_replace("\'", "''", $icq) . "'
#
#-----[ IN-LINE BEFORE, ADD ]---------------------------
#
, user_birthday = " . $user_birthday . "
#
#-----[ FIND ]------------------------------------------
# this is a partial match
#
			$sql = "INSERT INTO " . USERS_TABLE . "	(user_id, username
				VALUES ($user_id, '" . str_replace("\'", "''", $username)
#
#-----[ IN-LINE FIND ]----------------------------------
#
, user_icq
#
#-----[ IN-LINE BEFORE, ADD ]---------------------------
#
, user_birthday
#
#-----[ IN-LINE FIND ]----------------------------------
#
, '" . str_replace("\'", "''", $icq) . "'
#
#-----[ IN-LINE BEFORE, ADD ]---------------------------
#
, " . sprintf('%02d%02d%04d',$bday_month,$bday_day,$bday_year) . "
#
#-----[ FIND ]------------------------------------------
#
	$icq = $userdata['user_icq'];
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	preg_match('/(..)(..)(....)/', sprintf('%08d',$userdata['user_birthday']), $bday_parts);
	$bday_month = $bday_parts[1];
	$bday_day = $bday_parts[2];
	$bday_year = $bday_parts[3];

#
#-----[ FIND ]------------------------------------------
# this is a partial match
#
	display_avatar_gallery($mode,
#
#-----[ IN-LINE FIND ]----------------------------------
#
$user_lang,
#
#-----[ IN-LINE AFTER, ADD ]----------------------------
#
 $bday_month, $bday_day, $bday_year,
#
#-----[ FIND ]------------------------------------------
#
		'CONFIRM_IMG' => $confirm_image,
#
#-----[ AFTER, ADD ]------------------------------------
#
		'BDAY_MONTH' => ($bday_month != 0) ? $bday_month : $lang['Default_Month'],
		'BDAY_DAY' => ($bday_day != 0) ? $bday_day : $lang['Default_Day'],
		'BDAY_YEAR' => ($bday_year != 0) ? $bday_year : $lang['Default_Year'],
#
#-----[ FIND ]------------------------------------------
#
		'LANGUAGE_SELECT' => language_select($user_lang, 'language'),
#
#-----[ AFTER, ADD ]------------------------------------
#
		'BIRTHMONTH_SELECT' => bday_month_select($bday_month, 'bday_month'),
		'BIRTHDAY_SELECT' => bday_day_select($bday_day, 'bday_day'),
		'BIRTHYEAR_SELECT' => bday_year_select($bday_year, 'bday_year'),
#
#-----[ FIND ]------------------------------------------
#
		'L_ICQ_NUMBER' => $lang['ICQ'],
#
#-----[ BEFORE, ADD ]-----------------------------------
#
		'L_CLEAR' => $lang['Clear'],
		'L_BIRTHDAY' => $lang['Birthday'],
		'L_MONTH' => $lang['Month'],
		'L_DAY' => $lang['Day'],
		'L_YEAR' => ( $board_config['bday_year'] ) ? $lang['Year'] : $lang['Year_Optional'],
		'L_OPTIONAL' => ( $board_config['bday_year'] ) ? '' : $lang['Optional'],
#
#-----[ FIND ]------------------------------------------
#
		'S_PROFILE_ACTION' => append_sid("profile.$phpEx"))
	);
#
#-----[ AFTER, ADD ]------------------------------------
#

	if ( !$board_config['bday_lock'] || $userdata['user_birthday'] == 0 )
	{
		$block = ( $board_config['bday_require'] == TRUE ) ? 'birthday_required' : 'birthday_optional';
		$template->assign_block_vars($block, array());
		$template->birthday_interface();
	}
#
#-----[ OPEN ]------------------------------------------
#
includes/usercp_avatar.php
#
#-----[ FIND ]------------------------------------------
# this is only a partial match
#
function display_avatar_gallery($mode
#
#-----[ IN-LINE FIND ]----------------------------------
#
&$language,
#
#-----[ IN-LINE AFTER, ADD ]----------------------------
#
 &$bday_month, &$bday_day, &$bday_year,
#
#-----[ FIND ]------------------------------------------
# this is only a partial match
#
$params = array(
#
#-----[ IN-LINE FIND ]----------------------------------
#
'language',
#
#-----[ IN-LINE AFTER, ADD ]----------------------------
#
 'bday_month', 'bday_day', 'bday_year',
#
#-----[ OPEN ]------------------------------------------
#
templates/subSilver/profile_add_body.tpl
#
#-----[ FIND ]------------------------------------------
#
	<!-- BEGIN switch_edit_profile -->
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	<!-- BEGIN birthday_required -->
	<tr>
	  <td class="row1"><span class="gen">{L_BIRTHDAY}: *</span></td>
	  <td class="row2">{BIRTHDAY_INTERFACE}</td>
	</tr>
	<!-- END birthday_required -->
#
#-----[ FIND ]------------------------------------------
#
	<tr> 
	  <td class="row1"><span class="gen">{L_SIGNATURE}:</span><br /><span class="gensmall">{L_SIGNATURE_EXPLAIN}<br /><br />{HTML_STATUS}<br />{BBCODE_STATUS}<br />{SMILIES_STATUS}</span></td>
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	<!-- BEGIN birthday_optional -->
	<tr>
	  <td class="row1"><span class="gen">{L_BIRTHDAY}:</span></td>
	  <td class="row2">{BIRTHDAY_INTERFACE}</td>
	</tr>
	<!-- END birthday_optional -->
#
# now we start editing the admin user management files
#-----[ OPEN ]------------------------------------------
#
admin/admin_users.php
#
#-----[ FIND ]------------------------------------------
#
		$icq = ( !empty($HTTP_POST_VARS['icq']) ) ? trim(strip_tags( $HTTP_POST_VARS['icq'] ) ) : '';
#
#-----[ BEFORE, ADD ]-----------------------------------
#
		$bday_year = ( !empty($HTTP_POST_VARS['bday_year']) ) ? $HTTP_POST_VARS['bday_year'] : 0;
		$bday_month = ( !empty($HTTP_POST_VARS['bday_month']) ) ? $HTTP_POST_VARS['bday_month'] : 0;
		$bday_day = ( !empty($HTTP_POST_VARS['bday_day']) ) ? $HTTP_POST_VARS['bday_day'] : 0;

#
#-----[ FIND ]------------------------------------------
#
		if ($signature != '')
#
#-----[ BEFORE, ADD ]-----------------------------------
#
		$empty_month = empty($bday_month) || $bday_month == $lang['Default_Month'];
		$empty_day = empty($bday_day) || $bday_day == $lang['Default_Day'];
		$empty_year = empty($bday_year) || $bday_year == $lang['Default_Year'];

		$temp_month = $empty_month ? 1 : $bday_month;
		$temp_day = $empty_day ? 1 : $bday_day;
		$temp_year = $empty_year ? 4 : $bday_year;

		switch (true)
		{
			case $board_config['bday_year'] && (($empty_month != $empty_day) || ($empty_day != $empty_year)):
			case !$board_config['bday_year'] && (($empty_month != $empty_day) || ($empty_day && !$empty_year)):
			case !@checkdate( $temp_month, $temp_day, $temp_year ) && (!$board_config['bday_lock'] || $userdata['user_birthday'] == 0):
				$error = TRUE;
				$error_msg .= ( ( !empty($error_msg) ) ? '<br />' : '' ) . $lang['Birthday_invalid'];
		}

#
#-----[ FIND ]------------------------------------------
# this is a partial match
#
			$sql = "UPDATE " . USERS_TABLE . "
				SET " . $username_sql
#
#-----[ IN-LINE FIND ]----------------------------------
#
, user_icq = '" . str_replace("\'", "''", $icq) . "'
#
#-----[ IN-LINE BEFORE, ADD ]---------------------------
#
, user_birthday = " . sprintf('%02d%02d%04d',$bday_month,$bday_day,$bday_year) . "
#
#-----[ FIND ]------------------------------------------
#
		$icq = $this_userdata['user_icq'];
#
#-----[ BEFORE, ADD ]-----------------------------------
#
		preg_match('/(..)(..)(....)/', sprintf('%08d',$this_userdata['user_birthday']), $bday_parts);
		$bday_month = $bday_parts[1];
		$bday_day = $bday_parts[2];
		$bday_year = $bday_parts[3];

#
#-----[ FIND ]------------------------------------------
#
			$s_hidden_fields .= '<input type="hidden" name="icq" value="' . str_replace("\"", "&quot;", $icq) . '" />';
#
#-----[ BEFORE, ADD ]-----------------------------------
#
			$s_hidden_fields .= '<input type="hidden" name="bday_years" value="' . $bday_years . '" />';
			$s_hidden_fields .= '<input type="hidden" name="bday_months" value="' . $bday_months . '" />';
			$s_hidden_fields .= '<input type="hidden" name="bday_days" value="' . $bday_days . '" />';
#
#-----[ FIND ]------------------------------------------
#
			'WEBSITE' => $website,
#
#-----[ AFTER, ADD ]------------------------------------
#
			'BDAY_MONTH' => ($bday_month != 0) ? $bday_month : $lang['Default_Month'],
			'BDAY_DAY' => ($bday_day != 0) ? $bday_day : $lang['Default_Day'],
			'BDAY_YEAR' => ($bday_year != 0) ? $bday_year : $lang['Default_Year'],
#
#-----[ FIND ]------------------------------------------
#
			'LANGUAGE_SELECT' => language_select($user_lang),
#
#-----[ AFTER, ADD ]------------------------------------
#
			'BIRTHMONTH_SELECT' => bday_month_select($bday_month, 'bday_month'),
			'BIRTHDAY_SELECT' => bday_day_select($bday_day, 'bday_day'),
			'BIRTHYEAR_SELECT' => bday_year_select($bday_year, 'bday_year'),
#
#-----[ FIND ]------------------------------------------
#
			'L_ICQ_NUMBER' => $lang['ICQ'],
#
#-----[ BEFORE, ADD ]-----------------------------------
#
			'L_CLEAR' => $lang['Clear'],
			'L_BIRTHDAY' => $lang['Birthday'],
			'L_MONTH' => $lang['Month'],
			'L_DAY' => $lang['Day'],
			'L_YEAR' => ( $board_config['bday_year'] ) ? $lang['Year'] : $lang['Year_Optional'],
			'L_OPTIONAL' => ( $board_config['bday_year'] ) ? '' : $lang['Optional'],
#
#-----[ FIND ]------------------------------------------
#
			'S_PROFILE_ACTION' => append_sid("admin_users.$phpEx"))
		);
#
#-----[ AFTER, ADD ]------------------------------------
#

	$block = ( $board_config['bday_require'] == TRUE ) ? 'birthday_required' : 'birthday_optional';
	$template->assign_block_vars($block, array());
	$template->birthday_interface();
#
#-----[ OPEN ]------------------------------------------
#
templates/subSilver/admin/user_edit_body.tpl
#
#-----[ FIND ]------------------------------------------
#
	<tr> 
	  <td class="row1"><span class="gen">{L_NEW_PASSWORD}: *</span><br />
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	<!-- BEGIN birthday_required -->
	  <tr>
	    <td class="row1"><span class="gen">{L_BIRTHDAY}: *</span></td>
	    <td class="row2">{BIRTHDAY_INTERFACE}</td>
	</tr>
	<!-- END birthday_required -->
#
#-----[ FIND ]------------------------------------------
#
	<tr> 
	  <td class="row1"><span class="gen">{L_SIGNATURE}</span><br />
		<span class="gensmall">{L_SIGNATURE_EXPLAIN}<br />
		<br />
		{HTML_STATUS}<br />
		{BBCODE_STATUS}<br />
		{SMILIES_STATUS}</span></td>
	  <td class="row2"> 
		<textarea class="post" name="signature" rows="6" cols="45">{SIGNATURE}</textarea>
	  </td>
	</tr>
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	<!-- BEGIN birthday_optional -->
	  <tr>
	    <td class="row1"><span class="gen">{L_BIRTHDAY}:</span></td>
	    <td class="row2">{BIRTHDAY_INTERFACE}</td>
	</tr>
	<!-- END birthday_optional -->
#
# the following edits are what makes the age appear on the main page
#-----[ OPEN ]------------------------------------------
#
index.php
#
#-----[ FIND ]------------------------------------------
#
	while( $row = $db->sql_fetchrow($result) )
	{
		$forum_moderators[$row['forum_id']][] = '<a href="' . append_sid("groupcp.$phpEx?" . POST_GROUPS_URL . "=" . $row['group_id']) . '">' . $row['group_name'] . '</a>';
	}
	$db->sql_freeresult($result);
#
#-----[ AFTER, ADD ]------------------------------------
#

	$sql = "SELECT user_id, username, user_birthday, user_level 
		FROM " . USERS_TABLE . " 
		WHERE user_birthday >= " . gmdate('md0000',time() + (3600 * $board_config['board_timezone'])) . " 
			AND user_birthday <= " . gmdate('md9999',time() + (3600 * $board_config['board_timezone']))." 
			AND user_active = 1 
		ORDER BY username DESC";
	if ( !($result = $db->sql_query($sql)) )
	{
		message_die(GENERAL_ERROR, 'Could not query members birthday information', '', __LINE__, __FILE__, $sql);
	}

	$user_birthdays = array();
	while ( $row = $db->sql_fetchrow($result) )
	{
		$bday_year = $row['user_birthday'] % 10000;
		$age = ( $bday_year ) ? ' ('.(gmdate('Y')-$bday_year).')' : '';
		$color = '';
		if ( $row['user_level'] == ADMIN )
		{
			$color = ' style="color:#' . $theme['fontcolor3'] . '"';
		}
		else if ( $row['user_level'] == MOD )
		{
			$color = ' style="color:#' . $theme['fontcolor2'] . '"';
		}
		$user_birthdays[] = '<a href="' . append_sid("profile.$phpEx?mode=viewprofile&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '"' . $color . '>' . $row['username'] . '</a>' . $age;
	}
	$db->sql_freeresult($result);

	$birthdays = (!empty($user_birthdays)) ?
		sprintf($lang['Congratulations'],implode(', ',$user_birthdays)) :
		$lang['No_birthdays'];

	if ( $board_config['bday_lookahead'] != -1 )
	{
		$start = gmdate('md9999',strtotime('+'.$board_config['bday_lookahead'].' day') + (3600 * $board_config['board_timezone']));
		$end = gmdate('md0000',strtotime('+1 day') + (3600 * $board_config['board_timezone']));
		$operator = ($start > $end) ? 'AND' : 'OR';
		$sql = "SELECT user_id, username, user_birthday, user_level 
			FROM " . USERS_TABLE . " 
			WHERE (user_birthday <= $start 
				$operator user_birthday >= $end)
				AND user_birthday <> 0 
				AND user_active = 1 
			ORDER BY username DESC";
		if ( !($result = $db->sql_query($sql)) )
		{
			message_die(GENERAL_ERROR, 'Could not query upcoming birthday information', '', __LINE__, __FILE__, $sql);
		}
		$upcoming_birthdays = array();
		while ( $row = $db->sql_fetchrow($result) )
		{
			$bday_year = $row['user_birthday'] % 10000;
			$age = ( $bday_year ) ? ' ('.(gmdate('Y')-$bday_year).')' : '';
			$color = '';
			if ( $row['user_level'] == ADMIN )
			{
				$color = ' style="color:#' . $theme['fontcolor3'] . '"';
			}
			else if ( $row['user_level'] == MOD )
			{
				$color = ' style="color:#' . $theme['fontcolor2'] . '"';
			}
			$upcoming_birthdays[] = '<a href="' . append_sid("profile.$phpEx?mode=viewprofile&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '"' . $color . '>' . $row['username'] . '</a>' . $age;
		}

		$upcoming = (!empty($upcoming_birthdays)) ?
			sprintf($lang['Upcoming_birthdays'],$board_config['bday_lookahead'],implode(', ',$upcoming_birthdays)) :
			sprintf($lang['No_upcoming'],$board_config['bday_lookahead']);
	}

	if ( !empty($user_birthdays) || !empty($upcoming_birthdays) || $board_config['bday_show'] )
	{
		$template->assign_block_vars('birthdays',array());
		if ( !empty($upcoming_birthdays) || $board_config['bday_show'] )
		{
			$template->assign_block_vars('birthdays.upcoming',array());
		}
	}
#
#-----[ FIND ]------------------------------------------
#
		'NEWEST_USER' => sprintf($lang['Newest_user'], '<a href="' . append_sid("profile.$phpEx?mode=viewprofile&amp;" . POST_USERS_URL . "=$newest_uid") . '">', $newest_user, '</a>'), 
#
#-----[ AFTER, ADD ]------------------------------------
#
		'BIRTHDAYS' => $birthdays,
		'UPCOMING' => $upcoming,
#
#-----[ FIND ]------------------------------------------
#
		'L_FORUM' => $lang['Forum'],
#
#-----[ BEFORE, ADD ]-----------------------------------
#
		'L_TODAYS_BIRTHDAYS' => $lang['Todays_Birthdays'],
		'L_VIEW_BIRTHDAYS' => $lang['View_Birthdays'],

#
#-----[ OPEN ]------------------------------------------
#
templates/subSilver/index_body.tpl
#
#-----[ FIND ]------------------------------------------
#
  <tr> 
	<td class="row1" align="left"><span class="gensmall">{TOTAL_USERS_ONLINE} &nbsp; [ {L_WHOSONLINE_ADMIN} ] &nbsp; [ {L_WHOSONLINE_MOD} ]<br />{RECORD_USERS}<br />{LOGGED_IN_USER_LIST}</span></td>
  </tr>
#
#-----[ AFTER, ADD ]------------------------------------
#
  <!-- BEGIN birthdays -->
  <tr> 
	<td class="catHead" colspan="2" height="28"><span class="cattitle">{L_TODAYS_BIRTHDAYS}</span></td>
  </tr>
  <tr> 
	<td class="row1" align="center" valign="middle"><img src="templates/subSilver/images/icon_birthday.gif" alt="{L_VIEW_BIRTHDAYS}" /></td>
	<td class="row1" align="left" width="100%">
	  <span class="gensmall">{BIRTHDAYS}
	  <!-- BEGIN upcoming -->
	  <br />{UPCOMING}
	  <!-- END upcoming -->
	  </span>
	</td>
  </tr>
  <!-- END birthdays -->
#
#-----[ OPEN ]------------------------------------------
#
includes/usercp_viewprofile.php
#
#-----[ FIND ]------------------------------------------
#
if ( !empty($profiledata['user_icq']) )
#
#-----[ BEFORE, ADD ]-----------------------------------
#
$birthday = '&nbsp;';
if ( !empty($profiledata['user_birthday']) )
{
	preg_match('/(..)(..)(....)/', sprintf('%08d',$profiledata['user_birthday']), $bday_parts);
	$bday_month = $bday_parts[1];
	$bday_day = $bday_parts[2];
	$bday_year = $bday_parts[3];
	// the next line converts $lang['DATE_FORMAT'] to something that'll work with years, as this MOD encodes them.  the preg_replace replaces things like ', Y' with '' when the year isn't
	// specified, to account for date formats that would result in strings like 'October 31, 2005'
	$birthday_format = ($bday_year != 0) ? str_replace(array('y','Y'),array($bday_year % 100,$bday_year),$lang['DATE_FORMAT']) : preg_replace('#[^djFmMnYy]*[Yy]#','',$lang['DATE_FORMAT']);
	$birthday = create_date($birthday_format, gmmktime(0,0,0,$bday_month,$bday_day,4), 0);
}

#
#-----[ FIND ]------------------------------------------
#
	'LOCATION' => ( $profiledata['user_from'] ) ? $profiledata['user_from'] : '&nbsp;',
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	'BIRTHDAY' => $birthday,
#
#-----[ FIND ]------------------------------------------
#
	'L_LOCATION' => $lang['Location'],
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	'L_BIRTHDAY' => $lang['Birthday'],
#
#-----[ OPEN ]------------------------------------------
#
templates/subSilver/profile_view_body.tpl
#
#-----[ FIND ]------------------------------------------
#
		<tr> 
		  <td valign="top" align="right" nowrap="nowrap"><span class="gen">{L_INTERESTS}:</span></td>
		  <td> <b><span class="gen">{INTERESTS}</span></b></td>
		</tr>
#
#-----[ AFTER, ADD ]------------------------------------
#
		<tr> 
		  <td valign="top" align="right" nowrap="nowrap"><span class="gen">{L_BIRTHDAY}:</span></td>
		  <td> <b><span class="gen">{BIRTHDAY}</span></b></td>
		</tr>

#
# the following edits allow the various options to be configured via the ACP.
#-----[ OPEN ]------------------------------------------
#
admin/admin_board.php
#
#-----[ FIND ]------------------------------------------
#
$cookie_secure_yes = ( $new['cookie_secure'] ) ? "checked=\"checked\"" : "";
#
#-----[ BEFORE, ADD ]-----------------------------------
#
$bday_show_yes = ( $new['bday_show'] ) ? "checked=\"checked\"" : "";
$bday_show_no = ( !$new['bday_show'] ) ? "checked=\"checked\"" : "";
$bday_require_yes = ( $new['bday_require'] ) ? "checked=\"checked\"" : "";
$bday_require_no = ( !$new['bday_require'] ) ? "checked=\"checked\"" : "";
$bday_year_yes = ( $new['bday_year'] ) ? "checked=\"checked\"" : "";
$bday_year_no = ( !$new['bday_year'] ) ? "checked=\"checked\"" : "";
$bday_lock_yes = ( $new['bday_lock'] ) ? "checked=\"checked\"" : "";
$bday_lock_no = ( !$new['bday_lock'] ) ? "checked=\"checked\"" : "";

#
#-----[ FIND ]------------------------------------------
#
	"L_COOKIE_SETTINGS" => $lang['Cookie_settings'], 
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	"L_BIRTHDAYS" => $lang['Birthdays'],
	"L_BDAY_SHOW" => $lang['bday_show'],
	"L_UNCONDITIONAL" => $lang['Unconditional'],
	"L_CONDITIONAL" => $lang['Conditional'],
	"L_BDAY_SHOW_EXPLAIN" => $lang['bday_show_explain'],
	"L_BDAY_REQUIRE" => $lang['bday_require'],
	"L_BDAY_REQUIRE_EXPLAIN" => $lang['bday_require_explain'],
	"L_BDAY_YEAR" => $lang['bday_year'],
	"L_BDAY_YEAR_EXPLAIN" => $lang['bday_year_explain'],
	"L_BDAY_LOCK" => $lang['bday_lock'],
	"L_BDAY_LOCK_EXPLAIN" => $lang['bday_lock_explain'],
	"L_BDAY_LOOKAHEAD" => $lang['bday_lookahead'],
	"L_BDAY_LOOKAHEAD_EXPLAIN" => $lang['bday_lookahead_explain'],
	"L_BDAY_AGE_RANGE" => $lang['bday_age_range'],
	"L_TO" => $lang['To'],
#
#-----[ FIND ]------------------------------------------
#
	"COOKIE_DOMAIN" => $new['cookie_domain'],
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	"BDAY_SHOW_YES" => $bday_show_yes,
	"BDAY_SHOW_NO" => $bday_show_no,
	"BDAY_REQUIRE_YES" => $bday_require_yes,
	"BDAY_REQUIRE_NO" => $bday_require_no,
	"BDAY_YEAR_YES" => $bday_year_yes,
	"BDAY_YEAR_NO" => $bday_year_no,
	"BDAY_LOCK_YES" => $bday_lock_yes,
	"BDAY_LOCK_NO" => $bday_lock_no,
	"BDAY_LOOKAHEAD" => $new['bday_lookahead'],
	"BDAY_MIN" => $new['bday_min'],
	"BDAY_MAX" => $new['bday_max'],

#
#-----[ OPEN ]------------------------------------------
#
templates/subSilver/admin/board_config_body.tpl
#
#-----[ FIND ]------------------------------------------
#
	<tr>
		<th class="thHead" colspan="2">{L_COOKIE_SETTINGS}</th>
	</tr>
#
#-----[ BEFORE, ADD ]-----------------------------------
#
	<tr>
		<th class="thHead" colspan="2">{L_BIRTHDAYS}</th>
	</tr>
	<tr>
		<td class="row1">{L_BDAY_REQUIRE}<br /><span class="gensmall">{L_BDAY_REQUIRE_EXPLAIN}</span></td>
		<td class="row2"><input type="radio" name="bday_require" value="1" {BDAY_REQUIRE_YES} />{L_YES}&nbsp; &nbsp;<input type="radio" name="bday_require" value="0" {BDAY_REQUIRE_NO} />{L_NO}</td>
	</tr>
	<tr>
		<td class="row1">{L_BDAY_YEAR}<br /><span class="gensmall">{L_BDAY_YEAR_EXPLAIN}</span></td>
		<td class="row2"><input type="radio" name="bday_year" value="1" {BDAY_YEAR_YES} />{L_YES}&nbsp; &nbsp;<input type="radio" name="bday_year" value="0" {BDAY_YEAR_NO} />{L_NO}</td>
	</tr>
	<tr>
		<td class="row1">{L_BDAY_LOCK}<br /><span class="gensmall">{L_BDAY_LOCK_EXPLAIN}</span></td>
		<td class="row2"><input type="radio" name="bday_lock" value="1" {BDAY_LOCK_YES} />{L_YES}&nbsp; &nbsp;<input type="radio" name="bday_lock" value="0" {BDAY_LOCK_NO} />{L_NO}</td>
	</tr>
	<tr>
		<td class="row1">{L_BDAY_LOOKAHEAD}<br /><span class="gensmall">{L_BDAY_LOOKAHEAD_EXPLAIN}</span></td>
		<td class="row2"><input class="post" type="text" size="2" maxlength="2" name="bday_lookahead" value="{BDAY_LOOKAHEAD}" /></td>
	</tr>
	<tr>
		<td class="row1">{L_BDAY_AGE_RANGE}</td>
		<td class="row2"><input class="post" type="text" size="2" maxlength="2" name="bday_min" value="{BDAY_MIN}" /> {L_TO} <input class="post" type="text" size="3" maxlength="3" name="bday_max" value="{BDAY_MAX}" /></td>
	</tr>
	<tr>
		<td class="row1">{L_BDAY_SHOW}<br /><span class="gensmall">{L_BDAY_SHOW_EXPLAIN}</span></td>
		<td class="row2"><input type="radio" name="bday_show" value="1" {BDAY_SHOW_YES} />{L_UNCONDITIONAL}&nbsp; &nbsp;<input type="radio" name="bday_show" value="0" {BDAY_SHOW_NO} />{L_CONDITIONAL}</td>
	</tr>
#
#-----[ SAVE/CLOSE ALL FILES ]--------------------------
#
# EoM