.
*/
declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\Controllers;
use Fisharebest\Webtrees\Date;
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\Fact;
use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\GedcomRecord;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Services\CalendarService;
use Fisharebest\Webtrees\Services\LocalizationService;
use Fisharebest\Webtrees\Tree;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Show anniveraries for events in a given day/month/year.
*/
class CalendarController extends AbstractBaseController
{
/** @var CalendarService */
private $calendar_service;
/** @var LocalizationService */
private $localization_service;
/**
* CalendarController constructor.
*
* @param CalendarService $calendar_service
* @param LocalizationService $localization_service
*/
public function __construct(CalendarService $calendar_service, LocalizationService $localization_service)
{
$this->calendar_service = $calendar_service;
$this->localization_service = $localization_service;
}
/**
* A form to request the page parameters.
*
* @param Request $request
*
* @return Response
*/
public function page(Request $request): Response
{
$cal = $request->get('cal', '');
$day = $request->get('day', '');
$month = $request->get('month', '');
$year = $request->get('year', '');
$view = $request->get('view', 'day');
$filterev = $request->get('filterev', 'BIRT-MARR-DEAT');
$filterof = $request->get('filterof', 'all');
$filtersx = $request->get('filtersx', '');
if ($cal . $day . $month . $year === '') {
// No date specified? Use the most likely calendar
$cal = $this->localization_service->calendar()->gedcomCalendarEscape();
}
// 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();
$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';
}
$title = I18N::translate('Anniversary calendar');
switch ($view) {
case 'day':
$title = I18N::translate('On this day…') . ' ' . $ged_date->display(false);
break;
case 'month':
$title = I18N::translate('In this month…') . ' ' . $ged_date->display(false, '%F %Y');
break;
case 'year':
$title = I18N::translate('In this year…') . ' ' . $ged_date->display(false, '%Y');
break;
}
return $this->viewResponse('calendar-page', [
'cal' => $cal,
'cal_date' => $cal_date,
'cal_month' => $cal_month,
'day' => $day,
'days_in_month' => $days_in_month,
'filterev' => $filterev,
'filterof' => $filterof,
'filtersx' => $filtersx,
'month' => $month,
'months' => $this->calendar_service->calendarMonthsInYear($cal, $year),
'title' => $title,
'today' => $today,
'today_month' => $today_month,
'view' => $view,
'year' => $year,
]);
}
/**
* Show anniveraries that occured on a given day/month/year.
*
* @param Request $request
* @param Tree $tree
* @param CalendarService $calendar_service
*
* @return Response
*/
public function calendar(Request $request, Tree $tree, CalendarService $calendar_service): Response
{
$CALENDAR_FORMAT = $tree->getPreference('CALENDAR_FORMAT');
$cal = $request->get('cal', '');
$day = $request->get('day', '');
$month = $request->get('month', '');
$year = $request->get('year', '');
$view = $request->get('view', '');
$filterev = $request->get('filterev', 'BIRT-MARR-DEAT');
$filterof = $request->get('filterof', 'all');
$filtersx = $request->get('filtersx', '');
if ($cal . $day . $month . $year === '') {
// No date specified? Use the most likely calendar
$cal = $this->localization_service->calendar()->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}");
}
}
$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();
// Extract values from date
$days_in_month = $cal_date->daysInMonth();
$days_in_week = $cal_date->daysInWeek();
// Invalid dates? Go to monthly view, where they'll be found.
if ($cal_date->d > $days_in_month && $view === 'day') {
$view = 'month';
}
/** @var Fact[]|Fact[][] $found_facts */
$found_facts = [];
switch ($view) {
case 'day':
$found_facts = $this->applyFilter($calendar_service->getAnniversaryEvents($cal_date->minJD, $filterev, $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 ($this->applyFilter($calendar_service->getAnniversaryEvents($jd, $filterev, $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 = $this->applyFilter($calendar_service->getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), $filterev, $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] = $this->calendarFactText($fact, true);
} else {
$indis[$xref] .= '
' . $this->calendarFactText($fact, true);
}
} elseif ($record instanceof Family) {
if (empty($indis[$xref])) {
$fams[$xref] = $this->calendarFactText($fact, true);
} else {
$fams[$xref] .= '
' . $this->calendarFactText($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] = $this->calendarFactText($fact, false);
} else {
$cal_facts[$d][$xref] .= '
' . $this->calendarFactText($fact, false);
}
}
}
break;
}
ob_start();
switch ($view) {
case 'year':
case 'day':
echo '
| ', I18N::translate('Individuals'), ' | '; echo '', I18N::translate('Families'), ' | '; echo '
';
$content = $this->calendarListText($indis, '
| ';
echo '';
$content = $this->calendarListText($fams, '
| ';
echo '
| ', I18N::translate('Total individuals: %s', count($indis)), ' | '; echo '', I18N::translate('Total families: %s', count($fams)), ' | '; echo '
| ', $day_name, ' | '; } else { echo '', $day_name, ' | '; } } echo '
|---|---|
| ';
if ($d < 1 || $d > $days_in_month) {
if (count($cal_facts[0]) > 0) {
echo '', I18N::translate('Day not set'), ' '; echo ' ';
echo $this->calendarListText($cal_facts[0], '', '', $tree);
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 $this->calendarListText($cal_facts[$d], '', '', $tree);
echo ' ';
}
echo ' | ';
if (($d + $cal_date->minJD - $week_start) % $days_in_week === 0) {
echo '