. */ namespace Fisharebest\Webtrees; use Fisharebest\Webtrees\Controller\PageController; use Fisharebest\Webtrees\Date\FrenchDate; use Fisharebest\Webtrees\Date\GregorianDate; use Fisharebest\Webtrees\Date\HijriDate; use Fisharebest\Webtrees\Date\JalaliDate; use Fisharebest\Webtrees\Date\JewishDate; use Fisharebest\Webtrees\Date\JulianDate; use Fisharebest\Webtrees\Functions\FunctionsDb; use Fisharebest\Webtrees\Functions\FunctionsPrint; /** @global Tree $WT_TREE */ global $WT_TREE; require 'includes/session.php'; $CALENDAR_FORMAT = $WT_TREE->getPreference('CALENDAR_FORMAT'); $cal = Filter::get('cal', '@#D[A-Z ]+@'); $day = Filter::get('day', '\d\d?'); $month = Filter::get('month', '[A-Z]{3,5}'); $year = Filter::get('year', '\d{1,4}(?: B\.C\.)?|\d\d\d\d\/\d\d|\d+(-\d+|[?]+)?'); $view = Filter::get('view', 'day|month|year', 'day'); $filterev = Filter::get('filterev', '[_A-Z-]*', 'BIRT-MARR-DEAT'); $filterof = Filter::get('filterof', 'all|living|recent', 'all'); $filtersx = Filter::get('filtersx', '[MF]', ''); if ($cal . $day . $month . $year === '') { // No date specified? Use the most likely calendar $cal = I18N::defaultCalendar()->gedcomCalendarEscape(); } // Create a CalendarDate from the parameters // We cannot display new-style/old-style years, so convert to new style if (preg_match('/^(\d\d)\d\d\/(\d\d)$/', $year, $match)) { $year = $match[1] . $match[2]; } // advanced-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 Date("FROM {$cal} {$match[1]} TO {$cal} {$match[2]}"); $view = '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 Date("FROM {$cal} {$y1} TO {$cal} {$y2}"); $view = 'year'; } else { if ($year < 0) { $year = (-$year) . ' B.C.'; } // need BC to parse date $ged_date = new Date("{$cal} {$day} {$month} {$year}"); $year = $ged_date->minimumDate()->y; // need negative year for year entry field. } } $cal_date = $ged_date->minimumDate(); // 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 && $view === 'day') { $view = 'month'; } // All further uses of $cal are to generate URLs $cal = rawurlencode($cal); $controller = new PageController; $controller->setPageTitle(I18N::translate('Anniversary calendar')); switch ($view) { case 'day': $controller->setPageTitle(I18N::translate('On this day…') . ' ' . $ged_date->display(false)); break; case 'month': $controller->setPageTitle(I18N::translate('In this month…') . ' ' . $ged_date->display(false, '%F %Y')); break; case 'year': $controller->setPageTitle(I18N::translate('In this year…') . ' ' . $ged_date->display(false, '%Y')); break; } // Only generate the content for interactive users (not search robots). if (Filter::getBool('ajax') && Session::has('initiated')) { ?>

getPageTitle() ?>

| | $cal_name) { $tmp = $cal_date->convertToCalendar($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->format('%O'); echo '', $cal_name, ''; } } } ?>
minJD, $filterev, $WT_TREE), $filterof, $filtersx); break; case 'month': $cal_date->d = 0; $cal_date->setJdFromYmd(); // Make a separate list for each day. Unspecified/invalid days go in day 0. for ($d = 0; $d <= $days_in_month; ++$d) { $found_facts[$d] = []; } // Fetch events for each day for ($jd = $cal_date->minJD; $jd <= $cal_date->maxJD; ++$jd) { foreach (apply_filter(FunctionsDb::getAnniversaryEvents($jd, $filterev, $WT_TREE), $filterof, $filtersx) as $fact) { $tmp = $fact->getDate()->minimumDate(); if ($tmp->d >= 1 && $tmp->d <= $tmp->daysInMonth()) { // If the day is valid (for its own calendar), display it in the // anniversary day (for the display calendar). $found_facts[$jd - $cal_date->minJD + 1][] = $fact; } else { // Otherwise, display it in the "Day not set" box. $found_facts[0][] = $fact; } } } break; case 'year': $cal_date->m = 0; $cal_date->setJdFromYmd(); $found_facts = apply_filter(FunctionsDb::getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), $filterev, $WT_TREE), $filterof, $filtersx); // Eliminate duplicates (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900) $found_facts = array_unique($found_facts); break; } // Group the facts by family/individual $indis = []; $fams = []; $cal_facts = []; switch ($view) { case 'year': case 'day': foreach ($found_facts as $fact) { $record = $fact->getParent(); $xref = $record->getXref(); if ($record instanceof Individual) { if (empty($indis[$xref])) { $indis[$xref] = calendar_fact_text($fact, true); } else { $indis[$xref] .= '
' . calendar_fact_text($fact, true); } } elseif ($record instanceof Family) { if (empty($indis[$xref])) { $fams[$xref] = calendar_fact_text($fact, true); } else { $fams[$xref] .= '
' . calendar_fact_text($fact, true); } } } break; case 'month': foreach ($found_facts as $d => $facts) { $cal_facts[$d] = []; foreach ($facts as $fact) { $xref = $fact->getParent()->getXref(); if (empty($cal_facts[$d][$xref])) { $cal_facts[$d][$xref] = calendar_fact_text($fact, false); } else { $cal_facts[$d][$xref] .= '
' . calendar_fact_text($fact, false); } } } break; } switch ($view) { case 'year': case 'day': $males = 0; $females = 0; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo '
', I18N::translate('Individuals'), '', I18N::translate('Families'), '
'; $content = calendar_list_text($indis, '
  • ', '
  • ', true); if ($content) { echo '
      ', $content, '
    '; } echo '
    '; $content = calendar_list_text($fams, '
  • ', '
  • ', true); if ($content) { echo '
      ', $content, '
    '; } echo '
    ', I18N::translate('Total individuals: %s', count($indis)); echo '
    '; echo ' ', $males, ' '; echo ' ', $females, ' '; if (count($indis) !== $males + $females) { echo ' ', count($indis) - $males - $females; } echo '
    ', I18N::translate('Total families: %s', count($fams)), '
    '; break; case 'month': // We use JD%7 = 0/Mon…6/Sun. Standard definitions use 0/Sun…6/Sat. $week_start = (I18N::firstDay() + 6) % 7; $weekend_start = (I18N::weekendStart() + 6) % 7; $weekend_end = (I18N::weekendEnd() + 6) % 7; // The french calendar has a 10-day week, which starts on primidi if ($days_in_week === 10) { $week_start = 0; $weekend_start = -1; $weekend_end = -1; } echo ''; for ($week_day = 0; $week_day < $days_in_week; ++$week_day) { $day_name = $cal_date->dayNames(($week_day + $week_start) % $days_in_week); if ($week_day == $weekend_start || $week_day == $weekend_end) { echo ''; } else { echo ''; } } echo ''; echo ''; echo ''; // Print days 1 to 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 + $cal_date->minJD - $week_start) % $days_in_week === 0) { echo ''; } } echo ''; echo '
    ', $day_name, '', $day_name, '
    '; if ($d < 1 || $d > $days_in_month) { if (count($cal_facts[0]) > 0) { echo '', I18N::translate('Day not set'), '
    '; echo '
    '; echo calendar_list_text($cal_facts[0], '', '', false); echo '
    '; $cal_facts[0] = []; } } else { // Format the day number using the calendar $tmp = new Date($cal_date->format("%@ {$d} %O %E")); $d_fmt = $tmp->minimumDate()->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) { switch ($convcal) { case 'french': $alt_date = new FrenchDate($cal_date->minJD + $d - 1); break; case 'gregorian': $alt_date = new GregorianDate($cal_date->minJD + $d - 1); break; case 'jewish': $alt_date = new JewishDate($cal_date->minJD + $d - 1); break; case 'julian': $alt_date = new JulianDate($cal_date->minJD + $d - 1); break; case 'hijri': $alt_date = new HijriDate($cal_date->minJD + $d - 1); break; case 'jalali': $alt_date = new JalaliDate($cal_date->minJD + $d - 1); break; default: break 2; } if (get_class($alt_date) !== get_class($cal_date) && $alt_date->inValidRange()) { echo '' . $alt_date->format('%j %M') . ''; // Just show the first conversion break; } } echo '
    '; echo calendar_list_text($cal_facts[$d], '', '', false); echo '
    '; } echo '
    '; break; } echo '
    '; //close "calendar-page" return; } $ajax_url = Html::url('calendar.php', [ 'ged' => $WT_TREE->getName(), 'cal' => $cal, 'day' => $day, 'month' => $month, 'year' => $year, 'view' => $view, 'filterev' => $filterev, 'filterof' => $filterof, 'filtersx' => $filtersx, 'ajax' => 1, ]); $controller->pageHeader(); ?>
    getParent(); if ($filtersx) { // Filter on sex if ($record instanceof Individual && $filtersx !== $record->getSex()) { continue; } // Can't display families if the sex filter is on. if ($record instanceof Family) { continue; } } // Filter living individuals if ($filterof === 'living') { if ($record instanceof Individual && $record->isDead()) { continue; } if ($record instanceof Family) { $husb = $record->getHusband(); $wife = $record->getWife(); if ($husb && $husb->isDead() || $wife && $wife->isDead()) { continue; } } } // Filter on recent events if ($filterof === 'recent' && $fact->getDate()->maximumJulianDay() < $hundred_years) { continue; } $filtered[] = $fact; } return $filtered; } /** * Format an anniversary display. * * @param Fact $fact * @param bool $show_places * * @return string */ function calendar_fact_text(Fact $fact, $show_places) { $text = $fact->getLabel() . ' — ' . $fact->getDate()->display(true, null, false); if ($fact->anniv) { $text .= ' (' . I18N::translate('%s year anniversary', $fact->anniv) . ')'; } if ($show_places && $fact->getAttribute('PLAC')) { $text .= ' — ' . $fact->getAttribute('PLAC'); } return $text; } /** * Format a list of facts for display * * @param Fact[] $list * @param string $tag1 * @param string $tag2 * @param bool $show_sex_symbols * * @return string */ function calendar_list_text($list, $tag1, $tag2, $show_sex_symbols) { global $males, $females, $WT_TREE; $html = ''; foreach ($list as $id => $facts) { $tmp = GedcomRecord::getInstance($id, $WT_TREE); $html .= $tag1 . '' . $tmp->getFullName() . ' '; if ($show_sex_symbols && $tmp instanceof Individual) { switch ($tmp->getSex()) { case 'M': $html .= ''; ++$males; break; case 'F': $html .= ''; ++$females; break; default: $html .= ''; break; } } $html .= '
    ' . $facts . '
    ' . $tag2; } return $html; }