setPageTitle(WT_I18N::translate('Anniversary calendar'));
$controller->pageHeader();
$cal =safe_GET('cal', '@#D[A-Z ]+@');
$day =safe_GET('day', '[0-9]+');
$month =safe_GET('month', '[A-Z]{3,5}');
$year =safe_GET('year', '[0-9]+');
$action =safe_GET('action', array('year', 'today', 'calendar'), 'today');
$filterev=safe_GET('filterev', array('all', 'bdm', WT_REGEX_TAG), 'bdm');
$filterof=safe_GET('filterof', array('all', 'living', 'recent'), 'all');
$filtersx=safe_GET('filtersx', array('M', 'F'), '');
if ($cal.$day.$month.$year=='') {
// No date specified? Use the most likely calendar
switch (WT_LOCALE) {
case 'fa': $cal='@#DJALALI@'; break;
case 'ar': $cal='@#DHIJRI@'; break;
case 'he': $cal='@#DHEBREW@'; break;
default: $cal='@#DGREGORIAN@'; break;
}
}
// Create a WT_Date_Calendar from the parameters
// advance-year "year range"
if (preg_match('/^(\d+)-(\d+)$/', $year, $match)) {
if (strlen($match[1]) > strlen($match[2]))
$match[2]=substr($match[1], 0, strlen($match[1])-strlen($match[2])).$match[2];
$ged_date=new WT_Date("FROM {$cal} {$match[1]} TO {$cal} {$match[2]}");
$action='year';
} else
// advanced-year "decade/century wildcard"
if (preg_match('/^(\d+)(\?+)$/', $year, $match)) {
$y1=$match[1].str_replace('?', '0', $match[2]);
$y2=$match[1].str_replace('?', '9', $match[2]);
$ged_date=new WT_Date("FROM {$cal} {$y1} TO {$cal} {$y2}");
$action='year';
} else {
if ($year<0)
$year=(-$year)."B.C."; // need BC to parse date
$ged_date=new WT_Date("{$cal} {$day} {$month} {$year}");
$year=$ged_date->date1->y; // need negative year for year entry field.
}
$cal_date=&$ged_date->date1;
// Invalid month? Pick a sensible one.
if ($cal_date instanceof WT_Date_Jewish && $cal_date->m==7 && $cal_date->y!=0 && !$cal_date->IsLeapYear())
$cal_date->m=6;
// Fill in any missing bits with todays date
$today=$cal_date->Today();
if ($cal_date->d==0) $cal_date->d=$today->d;
if ($cal_date->m==0) $cal_date->m=$today->m;
if ($cal_date->y==0) $cal_date->y=$today->y;
$cal_date->SetJDfromYMD();
if ($year==0)
$year=$cal_date->y;
// Extract values from date
$days_in_month=$cal_date->DaysInMonth();
$days_in_week=$cal_date->DaysInWeek();
$cal_month=$cal_date->Format('%O');
$today_month=$today->Format('%O');
// Invalid dates? Go to monthly view, where they'll be found.
if ($cal_date->d>$days_in_month && $action=='today')
$action='calendar';
echo '
';
// Calendar form
echo '
';
echo "
";
// Day/Month/Year and calendar selector
echo '| ';
if ($action=='today') {
echo '', WT_I18N::translate('View Day'), '';
} else {
echo "d}&month={$cal_month}&year={$cal_date->y}&filterev={$filterev}&filterof={$filterof}&filtersx={$filtersx}&action=today\">", WT_I18N::translate('View Day'), "";
}
if ($action=='calendar') {
echo ' | ', WT_I18N::translate('View Month'), '';
} else {
echo " | d}&month={$cal_month}&year={$cal_date->y}&filterev={$filterev}&filterof={$filterof}&filtersx={$filtersx}&action=calendar\">", WT_I18N::translate('View Month'), "";
}
if ($action=='year') {
echo ' | ', WT_I18N::translate('View Year'), '';
} else {
echo " | d}&month={$cal_month}&year={$cal_date->y}&filterev={$filterev}&filterof={$filterof}&filtersx={$filtersx}&action=year\">", WT_I18N::translate('View Year'), "";
}
echo ' | ';
$n=0;
foreach (array(
'gregorian'=>WT_Date_Gregorian::calendarName(),
'julian' =>WT_Date_Julian::calendarName(),
'jewish' =>WT_Date_Jewish::calendarName(),
'french' =>WT_Date_French::calendarName(),
'hijri' =>WT_Date_Hijri::calendarName(),
'jalali' =>WT_Date_Jalali::calendarName(),
) as $newcal=>$cal_name) {
$tmp=$cal_date->convert_to_cal($newcal);
if ($tmp->InValidRange()) {
if ($n++) {
echo ' | ';
}
if (get_class($tmp)==get_class($cal_date)) {
echo "{$cal_name}";
} else {
$newcalesc=urlencode($tmp->Format('%@'));
$tmpmonth=$tmp->FormatGedcomMonth();
echo "d}&month={$tmpmonth}&year={$tmp->y}&filterev={$filterev}&filterof={$filterof}&filtersx={$filtersx}&action={$action}\">{$cal_name}";
}
}
}
echo " |
";
echo "
";
// Convert event filter option to a list of gedcom event codes
if ($filterev=='all') {
$events='';
} else {
if ($filterev=='bdm') {
$events='BIRT MARR DEAT';
} else {
$events=$filterev;
}
}
// Fetch data for day/month/year views
switch ($action) {
case 'today':
$found_facts=apply_filter(get_anniversary_events($cal_date->minJD, $events), $filterof, $filtersx);
break;
case 'calendar':
$cal_date->d=0;
$cal_date->SetJDfromYMD();
// Make a separate list for each day. Unspecified/invalid days go in day 0.
$found_facts=array();
for ($d=0; $d<=$days_in_month; ++$d)
$found_facts[$d]=array();
// Fetch events for each day
for ($jd=$cal_date->minJD; $jd<=$cal_date->maxJD; ++$jd)
foreach (apply_filter(get_anniversary_events($jd, $events), $filterof, $filtersx) as $event) {
$tmp=$event['date']->MinDate();
if ($tmp->d>=1 && $tmp->d<=$tmp->DaysInMonth())
$d=$jd-$cal_date->minJD+1;
else
$d=0;
$found_facts[$d][]=$event;
}
break;
case 'year':
$cal_date->m=0;
$cal_date->SetJDfromYMD();
$found_facts=apply_filter(get_calendar_events($ged_date->MinJD(), $ged_date->MaxJD(), $events), $filterof, $filtersx);
// Eliminate duplictes (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900)
foreach ($found_facts as $key=>$value)
$found_facts[$key]=serialize($found_facts[$key]);
$found_facts=array_unique($found_facts);
foreach ($found_facts as $key=>$value)
$found_facts[$key]=unserialize($found_facts[$key]);
break;
}
// Group the facts by family/individual
switch ($action) {
case 'year':
case 'today':
$indis=array();
$fams=array();
foreach ($found_facts as $fact) {
$fact_text=calendar_fact_text($fact, true);
switch ($fact['objtype']) {
case 'INDI':
if (empty($indis[$fact['id']]))
$indis[$fact['id']]=$fact_text;
else
$indis[$fact['id']].='
'.$fact_text;
break;
case 'FAM':
if (empty($fams[$fact['id']]))
$fams[$fact['id']]=$fact_text;
else
$fams[$fact['id']].='
'.$fact_text;
break;
}
}
break;
case 'calendar':
$cal_facts=array();
foreach ($found_facts as $d=>$facts) {
$cal_facts[$d]=array();
foreach ($facts as $fact) {
$id=$fact['id'];
if (empty($cal_facts[$d][$id]))
$cal_facts[$d][$id]=calendar_fact_text($fact, false);
else
$cal_facts[$d][$id].='
'.calendar_fact_text($fact, false);
}
}
break;
}
switch ($action) {
case 'year':
case 'today':
echo '
';
// Table headings
echo '| ', WT_I18N::translate('Individuals'), ' | ';
echo '', WT_I18N::translate('Families'), ' | ';
echo '
';
// Table rows
$males=0;
$females=0;
$numfams=0;
echo '| ';
// Avoid an empty unordered list
ob_start();
echo calendar_list_text($indis, '', '', true);
$content = ob_get_clean();
if (!empty($content)) {
echo '';
}
echo ' | ';
echo '';
// Avoid an empty unordered list
ob_start();
echo calendar_list_text($fams, "", "", true);
$content = ob_get_clean();
if (!empty($content)) {
echo '';
}
echo ' | ';
echo "
";
// Table footers
echo '', WT_I18N::translate('Total individuals: %s', count($indis));
echo ' ';
echo ' ', $males, ' ';
echo ' ', $females, ' ';
if (count($indis)!=$males+$females) {
echo ' ', count($indis)-$males-$females;
}
echo ' | ';
echo '', WT_I18N::translate('Total families: %s', count($fams)), ' | ';
echo '
';
break;
case 'calendar':
// We use JD%7 = 0/Mon...6/Sun. Config files use 0/Sun...6/Sat. Add 6 to convert.
$week_start=($WEEK_START+6)%$days_in_week;
// The french calendar has a 10-day week, but our config only lets us choose
// mon-sun as a start day. Force french calendars to start on primidi
if ($days_in_week==10) {
$week_start=0;
}
echo "
";
for ($week_day=0; $week_day<$days_in_week; ++$week_day) {
$day_name=$cal_date->LONG_DAYS_OF_WEEK(($week_day+$week_start) % $days_in_week);
echo "| {$day_name} | ";
}
echo "
";
// Print days 1-n of the month...
// ...but extend to cover "empty" days before/after the month to make whole weeks.
// e.g. instead of 1 -> 30 (=30 days), we might have -1 -> 33 (=35 days)
$start_d=1-($cal_date->minJD-$week_start) % $days_in_week;
$end_d=$days_in_month+($days_in_week-($cal_date->maxJD-$week_start+1) % $days_in_week) % $days_in_week;
// Make sure that there is an empty box for any leap/missing days
if ($start_d==1 && $end_d==$days_in_month && count($found_facts[0])>0)
$end_d+=$days_in_week;
for ($d=$start_d; $d<=$end_d; ++$d) {
if (($d+$cal_date->minJD-$week_start) % $days_in_week==1)
echo "";
echo "";
if ($d<1 || $d>$days_in_month)
if (count($cal_facts[0])>0) {
echo "", WT_I18N::translate('Day not set'), " ";
echo "";
echo calendar_list_text($cal_facts[0], "", "", false);
echo " ";
$cal_facts[0]=array();
} else
echo ' ';
else {
// Format the day number using the calendar
$tmp=new WT_Date($cal_date->Format("%@ {$d} %O %E")); $d_fmt=$tmp->date1->Format('%j');
if ($d==$today->d && $cal_date->m==$today->m)
echo "{$d_fmt}";
else
echo "{$d_fmt}";
// Show a converted date
foreach (explode('_and_', $CALENDAR_FORMAT) as $convcal) {
$alt_date=$cal_date->convert_to_cal($convcal);
if (get_class($alt_date)!=get_class($cal_date)) {
list($alt_date->y, $alt_date->m, $alt_date->d)=$alt_date->JDtoYMD($cal_date->minJD+$d-1);
$alt_date->SetJDfromYMD();
echo "".$alt_date->Format("%j %M")."";
break;
}
}
echo '
';
echo calendar_list_text($cal_facts[$d], "", "", false);
echo ' ';
}
echo ' | ';
if (($d+$cal_date->minJD-$week_start) % $days_in_week==0) {
echo '
';
}
}
echo '
';
break;
}
echo '
'; //close "calendar-page"
/////////////////////////////////////////////////////////////////////////////////
// Filter a list of facts
/////////////////////////////////////////////////////////////////////////////////
function apply_filter($facts, $filterof, $filtersx) {
$filtered=array();
$hundred_years=WT_SERVER_JD-36525;
foreach ($facts as $fact) {
$tmp=WT_GedcomRecord::GetInstance($fact['id']);
// Filter on sex
if ($fact['objtype']=='INDI' && $filtersx!='' && $filtersx!=$tmp->getSex())
continue;
// Can't display families if the sex filter is on.
// TODO: but we could show same-sex partnerships....
if ($fact['objtype']=='FAM' && $filtersx!='')
continue;
// Filter on age of event
if ($filterof=='living') {
if ($fact['objtype']=='INDI' && $tmp->isDead())
continue;
if ($fact['objtype']=='FAM') {
$husb=$tmp->getHusband();
$wife=$tmp->getWife();
if (!empty($husb) && $husb->isDead())
continue;
if (!empty($wife) && $wife->isDead())
continue;
}
}
if ($filterof=='recent' && $fact['date']->MaxJD()<$hundred_years)
continue;
// Finally, check for privacy rules before adding fact.
if ($tmp->canDisplayDetails())
$filtered[]=$fact;
}
return $filtered;
}
////////////////////////////////////////////////////////////////////////////////
// Format a fact for display. Include the date, the event type, and optionally
// the place.
////////////////////////////////////////////////////////////////////////////////
function calendar_fact_text($fact, $show_places) {
$text=WT_Gedcom_Tag::getLabel($fact['fact']).' - '.$fact['date']->Display(true, "", array());
if ($fact['anniv']>0)
$text.=' ('.WT_I18N::translate('%s year anniversary', $fact['anniv']).')';
if ($show_places && !empty($fact['plac']))
$text.=' - '.$fact['plac'];
return $text;
}
////////////////////////////////////////////////////////////////////////////////
// Format a list of facts for display
////////////////////////////////////////////////////////////////////////////////
function calendar_list_text($list, $tag1, $tag2, $show_sex_symbols) {
global $males, $females;
foreach ($list as $id=>$facts) {
$tmp=WT_GedcomRecord::GetInstance($id);
echo $tag1, '