Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/Http/Requests/StoreCalendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public function rules()
'moon_name' => 'nullable|array',
'epoch_name' => 'nullable|array',
'season_name' => 'nullable|array',
'era_name' => 'nullable|array',
'era_start_year' => 'nullable|array',
Comment thread
spitfire305 marked this conversation as resolved.
'era_start_year.*' => 'nullable|integer',
'era_end_year' => 'nullable|array',
'era_end_year.*' => 'nullable|integer',
'show_birthdays' => 'boolean',
'template_id' => 'nullable',
'attribute' => ['array', new UniqueAttributeNames],
Expand Down
1 change: 1 addition & 0 deletions app/Http/Resources/CalendarResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public function toArray($request)
'weekdays' => json_decode($calendar->weekdays),
'years' => json_decode($calendar->years),
'seasons' => json_decode($calendar->seasons),
'eras' => json_decode($calendar->eras),
'moons' => json_decode($calendar->moons),
'start_offset' => $calendar->start_offset, // X year is a leap year
'suffix' => $calendar->suffix,
Expand Down
144 changes: 139 additions & 5 deletions app/Models/Calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* @property string $week_names
* @property string $month_aliases
* @property string $seasons
* @property string $eras
* @property string $moons
* @property string $reset
* @property string $format
Expand Down Expand Up @@ -57,6 +58,7 @@ class Calendar extends MiscModel
'suffix',
'skip_year_zero',
'epochs',
'eras',
'month_aliases',
'week_names',
'reset',
Expand Down Expand Up @@ -90,6 +92,8 @@ class Calendar extends MiscModel

protected array $loadedSeasons;

protected array $loadedEras;

protected array $loadedMoons;

protected array $loadedWeeks;
Expand Down Expand Up @@ -165,6 +169,42 @@ public function seasons(): array
return $this->loadedSeasons;
}

/**
* Get the calendar's eras
*/
public function eras(): array
{
if (! isset($this->loadedEras)) {
$this->loadedEras = json_decode(empty($this->eras) ? '[]' : strip_tags($this->eras), true);
}

return $this->loadedEras;
}

/**
* Find the active era for a given date
*/
public function getEraForDate(int|string $year, int $month, int $day): ?array
{
$year = (int) $year;
foreach ($this->eras() as $era) {
$hasStartYear = isset($era['start_year']) && $era['start_year'] !== '' && $era['start_year'] !== null;
$hasEndYear = isset($era['end_year']) && $era['end_year'] !== '' && $era['end_year'] !== null;

if ($hasStartYear && $year < (int) $era['start_year']) {
continue;
}

if ($hasEndYear && $year > (int) $era['end_year']) {
continue;
}

return $era;
}

return null;
}

/**
* Get the calendar's weeks
*/
Expand Down Expand Up @@ -234,7 +274,38 @@ public function currentDay(): int
}

/**
* Get the calendar's nice date
* Find eras that start or end on a given date
*
* @return array{start: ?array, end: ?array}|null
*/
public function getEraBoundary(int|string $year, int $month, int $day): ?array
{
if ($month !== 1 || $day !== 1) {
return null;
}

$year = (int) $year;
$result = null;

foreach ($this->eras() as $era) {
if (isset($era['start_year']) && $era['start_year'] !== '' && $era['start_year'] !== null
&& (int) $era['start_year'] === $year) {
$result ??= [];
$result['start'] = $era;
}

if (isset($era['end_year']) && $era['end_year'] !== '' && $era['end_year'] !== null
&& (int) $era['end_year'] === $year) {
$result ??= [];
$result['end'] = $era;
}
}

return $result;
}

/**
* Get the calendar's nice date, formatted with era if applicable
*/
public function niceDate(?string $date = null): string
{
Expand All @@ -247,22 +318,85 @@ public function niceDate(?string $date = null): string

[$year, $month, $day] = $this->dateArray($date);

// Replace month with real month, and year maybe
$months = $this->months();
$years = $this->years();

try {
$return = $day . ' ' .
(isset($months[$month - 1]) ? $months[$month - 1]['name'] : $month) . ', ' .
$monthName = isset($months[$month - 1]) ? $months[$month - 1]['name'] : $month;

$era = $this->getEraForDate($year, $month, $day);
if ($era) {
$anchor = isset($era['start_year']) && $era['start_year'] !== '' && $era['start_year'] !== null
? (int) $era['start_year']
: (int) $era['end_year'];
$relativeYear = abs((int) $year - $anchor) + 1;
if (! $this->hasYearZero() && ((int) $year < 0 && $anchor > 0 || (int) $year > 0 && $anchor < 0 || $anchor === 0)) {
$relativeYear--;
}

return $day . ' ' . $monthName . ', ' . $relativeYear . ' ' . $era['name'];
}

return $day . ' ' . $monthName . ', ' .
($years[$year] ?? $year) . ' ' .
$this->suffix;
} catch (Exception $e) { // @phpstan-ignore-line
return $this->date;
}
}

/**
* Get the calendar's date in the standard format, ignoring eras
*/
public function niceRawDate(?string $date = null): string
{
if (empty($date)) {
$date = $this->date;
}
if (empty($date)) {
return '';
}

[$year, $month, $day] = $this->dateArray($date);

$months = $this->months();
$years = $this->years();

return $return;
try {
if ($this->format) {
return Str::replace(
['d', 's', 'y', 'm', 'M'],
[$day, $this->suffix, $years[$year] ?? $year, $month, isset($months[$month - 1]) ? $months[$month - 1]['name'] : $month],
$this->format
);
}

return $day . ' ' .
(isset($months[$month - 1]) ? $months[$month - 1]['name'] : $month) . ', ' .
($years[$year] ?? $year) . ' ' .
$this->suffix;
} catch (Exception $e) { // @phpstan-ignore-line
return $this->date;
}
}

/**
* Check if the given date has an active era with date formatting
*/
public function dateHasEra(?string $date = null): bool
{
if (empty($date)) {
$date = $this->date;
}
if (empty($date)) {
return false;
}

[$year, $month, $day] = $this->dateArray($date);

return $this->getEraForDate($year, $month, $day) !== null;
}

/**
* Get a list of months for select fields
*/
Expand Down
58 changes: 56 additions & 2 deletions app/Models/Reminder.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,23 @@ public function isEntity(): bool
public function readableDate(): string
{
if (! isset($this->readableDate)) {
// Replace month with real month, and year maybe
$months = $this->calendar->months();
$years = $this->calendar->years();

try {
if ($this->calendar->format) {
// Check if this date falls within an era with date formatting
$era = $this->calendar->getEraForDate($this->year, $this->month, $this->day);
if ($era) {
$monthName = isset($months[$this->month - 1]) ? $months[$this->month - 1]['name'] : $this->month;
$anchor = isset($era['start_year']) && $era['start_year'] !== '' && $era['start_year'] !== null
? (int) $era['start_year']
: (int) $era['end_year'];
$relativeYear = abs((int) $this->year - $anchor) + 1;
if (! $this->calendar->hasYearZero() && ((int) $this->year < 0 && $anchor > 0 || (int) $this->year > 0 && $anchor < 0 || $anchor === 0)) {
$relativeYear--;
}
$this->readableDate = $this->day . ' ' . $monthName . ', ' . $relativeYear . ' ' . $era['name'];
} elseif ($this->calendar->format) {
$this->readableDate = Str::replace(
['d', 's', 'y', 'm', 'M'],
[$this->day, $this->calendar->suffix, $years[$this->year] ?? $this->year, $this->month, isset($months[$this->month - 1]) ? $months[$this->month - 1]['name'] : $this->month],
Expand All @@ -143,6 +154,49 @@ public function readableDate(): string
return $this->readableDate;
}

/**
* Get the date in the standard format, ignoring eras (for tooltips)
*/
public function readableRawDate(): string
{
$months = $this->calendar->months();
$years = $this->calendar->years();

try {
if ($this->calendar->format) {
return Str::replace(
['d', 's', 'y', 'm', 'M'],
[$this->day, $this->calendar->suffix, $years[$this->year] ?? $this->year, $this->month, isset($months[$this->month - 1]) ? $months[$this->month - 1]['name'] : $this->month],
$this->calendar->format
);
}

return $this->day . ' ' .
(isset($months[$this->month - 1]) ? $months[$this->month - 1]['name'] : $this->month) . ', ' .
($years[$this->year] ?? $this->year) . ' ' .
$this->calendar->suffix;
} catch (Exception $e) {
return $this->date();
}
}

/**
* Check if this reminder's date falls within an era with date formatting
*/
public function hasEra(): bool
{
return $this->calendar && $this->calendar->getEraForDate($this->year, $this->month, $this->day) !== null;
}

/**
* Get the tooltip title for this reminder's date.
* Returns the raw date when an era is active, or the calendar name otherwise.
*/
public function tooltipTitle(): string
{
return $this->hasEra() ? $this->readableRawDate() : $this->calendar->name;
}

/**
* Length of the event in a readable format (appends "days")
*/
Expand Down
10 changes: 10 additions & 0 deletions app/Renderers/CalendarRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,11 @@ public function buildForMonth()
$this->dayData['season'] = $this->seasonService->get($monthday);
}

$eraBoundary = $this->calendar->getEraBoundary($this->getYear(), $this->getMonth(), $day);
if ($eraBoundary) {
$this->dayData['era_boundary'] = $eraBoundary;
}

// Add recurring events that span multiple days from the previous call
$this->recurringReminders();
$this->addMoonReminders($day);
Expand Down Expand Up @@ -544,6 +549,11 @@ public function buildForYear(): array
if ($this->seasonService->has($monthday)) {
$this->dayData['season'] = $this->seasonService->get($monthday);
}

$eraBoundary = $this->calendar->getEraBoundary($this->getYear(), $monthNumber, $day);
if ($eraBoundary) {
$this->dayData['era_boundary'] = $eraBoundary;
}
$data[] = $this->dayData;

$totalDay++;
Expand Down
3 changes: 2 additions & 1 deletion app/Renderers/DatagridRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,8 @@ private function renderColumn(string|array $column, $model)
/** @var Journal $model */
if ($model->entity->calendarDate && $model->entity->calendarDate->calendar && $model->entity->calendarDate->calendar->entity) {
$reminder = $model->entity->calendarDate;
$content = '<a href="' . route('entities.show', [$this->campaign, $reminder->calendar->entity, 'month' => $reminder->month, 'year' => $reminder->year]) . '" class="text-link">' . $reminder->readableDate() . '</a>';
$tooltip = $reminder->hasEra() ? ' data-toggle="tooltip" data-title="' . e($reminder->readableRawDate()) . '"' : '';
$content = '<a href="' . route('entities.show', [$this->campaign, $reminder->calendar->entity, 'month' => $reminder->month, 'year' => $reminder->year]) . '" class="text-link"' . $tooltip . '>' . $reminder->readableDate() . '</a>';
}
} else {
// Exception
Expand Down
9 changes: 8 additions & 1 deletion app/Renderers/Layouts/Calendar/Reminder.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@ public function columns(): array
'date' => [
'key' => 'date',
'label' => __('events.fields.date'),
'render' => 'readableDate()',
'render' => function ($model) {
$date = $model->readableDate();
if ($model->hasEra()) {
return '<span data-toggle="tooltip" data-title="' . e($model->readableRawDate()) . '">' . $date . '</span>';
}

return $date;
},
],
'length' => [
'key' => 'length',
Expand Down
9 changes: 5 additions & 4 deletions app/Renderers/Layouts/Entity/Reminder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,23 @@ public function columns(): array
$columns = [
'calendar' => [
'key' => 'calendar.name',
'label' => 'entities.calendar',
'label' => __('entities.calendar'),
'render' => Standard::ENTITYLINK,
'with' => 'calendar',
],
'date' => [
'key' => 'date',
'label' => 'events.fields.date',
'label' => __('events.fields.date'),
'render' => function (Model $reminder) {
$params = '?year=' . $reminder->year . '&month=' . $reminder->month;
$tooltip = $reminder->hasEra() ? ' data-toggle="tooltip" data-title="' . e($reminder->readableRawDate()) . '"' : '';

return '<a href="' . $reminder->calendar->getLink() . $params . '" class="text-link">' . $reminder->readableDate() . '</a>';
return '<a href="' . $reminder->calendar->getLink() . $params . '" class="text-link"' . $tooltip . '>' . $reminder->readableDate() . '</a>';
},
],
'length' => [
'key' => 'length',
'label' => 'calendars.fields.length',
'label' => __('calendars.fields.length'),
'render' => function (Model $reminder) {
return trans_choice('calendars.fields.length_days', $reminder->length, ['count' => $reminder->length]);
},
Expand Down
Loading