mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
Compare commits
5 commits
91e222f7a6
...
8aea83703c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8aea83703c | ||
|
|
161200df44 | ||
|
|
23a7a217db | ||
|
|
c8f6dd3bec | ||
|
|
c1786dfb6d |
5 changed files with 185 additions and 7 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Liquid\Filters;
|
namespace App\Liquid\Filters;
|
||||||
|
|
||||||
|
use App\Liquid\Utils\ExpressionUtils;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Keepsuit\Liquid\Filters\FiltersProvider;
|
use Keepsuit\Liquid\Filters\FiltersProvider;
|
||||||
|
|
||||||
|
|
@ -22,4 +23,33 @@ class Date extends FiltersProvider
|
||||||
|
|
||||||
return Carbon::now()->subDays($days)->toDateString();
|
return Carbon::now()->subDays($days)->toDateString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date string with ordinal day (1st, 2nd, 3rd, etc.)
|
||||||
|
*
|
||||||
|
* @param string $dateStr The date string to parse
|
||||||
|
* @param string $strftimeExp The strftime format string with <<ordinal_day>> placeholder
|
||||||
|
* @return string The formatted date with ordinal day
|
||||||
|
*/
|
||||||
|
public function ordinalize(string $dateStr, string $strftimeExp): string
|
||||||
|
{
|
||||||
|
$date = Carbon::parse($dateStr);
|
||||||
|
$ordinalDay = $date->ordinal('day');
|
||||||
|
|
||||||
|
// Convert strftime format to PHP date format
|
||||||
|
$phpFormat = ExpressionUtils::strftimeToPhpFormat($strftimeExp);
|
||||||
|
|
||||||
|
// Split the format string by the ordinal day placeholder
|
||||||
|
$parts = explode('<<ordinal_day>>', $phpFormat);
|
||||||
|
|
||||||
|
if (count($parts) === 2) {
|
||||||
|
$before = $date->format($parts[0]);
|
||||||
|
$after = $date->format($parts[1]);
|
||||||
|
|
||||||
|
return $before.$ordinalDay.$after;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: if no placeholder found, just format normally
|
||||||
|
return $date->format($phpFormat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ class ExpressionUtils
|
||||||
if (self::evaluateCondition($condition['left'], $variable, $object)) {
|
if (self::evaluateCondition($condition['left'], $variable, $object)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::evaluateCondition($condition['right'], $variable, $object);
|
return self::evaluateCondition($condition['right'], $variable, $object);
|
||||||
|
|
||||||
case 'comparison':
|
case 'comparison':
|
||||||
|
|
@ -158,4 +159,52 @@ class ExpressionUtils
|
||||||
|
|
||||||
return $expression;
|
return $expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert strftime format string to PHP date format string
|
||||||
|
*
|
||||||
|
* @param string $strftimeFormat The strftime format string
|
||||||
|
* @return string The PHP date format string
|
||||||
|
*/
|
||||||
|
public static function strftimeToPhpFormat(string $strftimeFormat): string
|
||||||
|
{
|
||||||
|
$conversions = [
|
||||||
|
// Special Ruby format cases
|
||||||
|
'%N' => 'u', // Microseconds (Ruby) -> microseconds (PHP)
|
||||||
|
'%u' => 'u', // Microseconds (Ruby) -> microseconds (PHP)
|
||||||
|
'%-m' => 'n', // Month without leading zero (Ruby) -> month without leading zero (PHP)
|
||||||
|
'%-d' => 'j', // Day without leading zero (Ruby) -> day without leading zero (PHP)
|
||||||
|
'%-H' => 'G', // Hour without leading zero (Ruby) -> hour without leading zero (PHP)
|
||||||
|
'%-I' => 'g', // Hour 12h without leading zero (Ruby) -> hour 12h without leading zero (PHP)
|
||||||
|
'%-M' => 'i', // Minute without leading zero (Ruby) -> minute without leading zero (PHP)
|
||||||
|
'%-S' => 's', // Second without leading zero (Ruby) -> second without leading zero (PHP)
|
||||||
|
'%z' => 'O', // Timezone offset (Ruby) -> timezone offset (PHP)
|
||||||
|
'%Z' => 'T', // Timezone name (Ruby) -> timezone name (PHP)
|
||||||
|
|
||||||
|
// Standard strftime conversions
|
||||||
|
'%A' => 'l', // Full weekday name
|
||||||
|
'%a' => 'D', // Abbreviated weekday name
|
||||||
|
'%B' => 'F', // Full month name
|
||||||
|
'%b' => 'M', // Abbreviated month name
|
||||||
|
'%Y' => 'Y', // Full year (4 digits)
|
||||||
|
'%y' => 'y', // Year without century (2 digits)
|
||||||
|
'%m' => 'm', // Month as decimal number (01-12)
|
||||||
|
'%d' => 'd', // Day of month as decimal number (01-31)
|
||||||
|
'%H' => 'H', // Hour in 24-hour format (00-23)
|
||||||
|
'%I' => 'h', // Hour in 12-hour format (01-12)
|
||||||
|
'%M' => 'i', // Minute as decimal number (00-59)
|
||||||
|
'%S' => 's', // Second as decimal number (00-59)
|
||||||
|
'%p' => 'A', // AM/PM
|
||||||
|
'%P' => 'a', // am/pm
|
||||||
|
'%j' => 'z', // Day of year as decimal number (001-366)
|
||||||
|
'%w' => 'w', // Weekday as decimal number (0-6, Sunday is 0)
|
||||||
|
'%U' => 'W', // Week number of year (00-53, Sunday is first day)
|
||||||
|
'%W' => 'W', // Week number of year (00-53, Monday is first day)
|
||||||
|
'%c' => 'D M j H:i:s Y', // Date and time representation
|
||||||
|
'%x' => 'm/d/Y', // Date representation
|
||||||
|
'%X' => 'H:i:s', // Time representation
|
||||||
|
];
|
||||||
|
|
||||||
|
return str_replace(array_keys($conversions), array_values($conversions), $strftimeFormat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -216,15 +216,15 @@ class Plugin extends Model
|
||||||
*/
|
*/
|
||||||
private function applyLiquidReplacements(string $template): string
|
private function applyLiquidReplacements(string $template): string
|
||||||
{
|
{
|
||||||
$replacements = [
|
|
||||||
'date: "%N"' => 'date: "u"',
|
$replacements = [];
|
||||||
'date: "%u"' => 'date: "u"',
|
|
||||||
'%-m/%-d/%Y' => 'm/d/Y',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Apply basic replacements
|
// Apply basic replacements
|
||||||
$template = str_replace(array_keys($replacements), array_values($replacements), $template);
|
$template = str_replace(array_keys($replacements), array_values($replacements), $template);
|
||||||
|
|
||||||
|
// Convert Ruby/strftime date formats to PHP date formats
|
||||||
|
$template = $this->convertDateFormats($template);
|
||||||
|
|
||||||
// Convert {% render "template" with %} syntax to {% render "template", %} syntax
|
// Convert {% render "template" with %} syntax to {% render "template", %} syntax
|
||||||
$template = preg_replace(
|
$template = preg_replace(
|
||||||
'/{%\s*render\s+([^}]+?)\s+with\s+/i',
|
'/{%\s*render\s+([^}]+?)\s+with\s+/i',
|
||||||
|
|
@ -237,7 +237,7 @@ class Plugin extends Model
|
||||||
// Converts to: {% assign temp_filtered = collection | filter: "key", "value" %}{% for item in temp_filtered %}
|
// Converts to: {% assign temp_filtered = collection | filter: "key", "value" %}{% for item in temp_filtered %}
|
||||||
$template = preg_replace_callback(
|
$template = preg_replace_callback(
|
||||||
'/{%\s*for\s+(\w+)\s+in\s+([^|%}]+)\s*\|\s*([^%}]+)%}/',
|
'/{%\s*for\s+(\w+)\s+in\s+([^|%}]+)\s*\|\s*([^%}]+)%}/',
|
||||||
function ($matches): string {
|
function (array $matches): string {
|
||||||
$variableName = mb_trim($matches[1]);
|
$variableName = mb_trim($matches[1]);
|
||||||
$collection = mb_trim($matches[2]);
|
$collection = mb_trim($matches[2]);
|
||||||
$filter = mb_trim($matches[3]);
|
$filter = mb_trim($matches[3]);
|
||||||
|
|
@ -251,6 +251,40 @@ class Plugin extends Model
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Ruby/strftime date formats to PHP date formats in Liquid templates
|
||||||
|
*/
|
||||||
|
private function convertDateFormats(string $template): string
|
||||||
|
{
|
||||||
|
// Handle date filter formats: date: "format" or date: 'format'
|
||||||
|
$template = preg_replace_callback(
|
||||||
|
'/date:\s*(["\'])([^"\']+)\1/',
|
||||||
|
function (array $matches): string {
|
||||||
|
$quote = $matches[1];
|
||||||
|
$format = $matches[2];
|
||||||
|
$convertedFormat = \App\Liquid\Utils\ExpressionUtils::strftimeToPhpFormat($format);
|
||||||
|
|
||||||
|
return 'date: '.$quote.$convertedFormat.$quote;
|
||||||
|
},
|
||||||
|
$template
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle l_date filter formats: l_date: "format" or l_date: 'format'
|
||||||
|
$template = preg_replace_callback(
|
||||||
|
'/l_date:\s*(["\'])([^"\']+)\1/',
|
||||||
|
function (array $matches): string {
|
||||||
|
$quote = $matches[1];
|
||||||
|
$format = $matches[2];
|
||||||
|
$convertedFormat = \App\Liquid\Utils\ExpressionUtils::strftimeToPhpFormat($format);
|
||||||
|
|
||||||
|
return 'l_date: '.$quote.$convertedFormat.$quote;
|
||||||
|
},
|
||||||
|
(string) $template
|
||||||
|
);
|
||||||
|
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve Liquid variables in a template string using the Liquid template engine
|
* Resolve Liquid variables in a template string using the Liquid template engine
|
||||||
*
|
*
|
||||||
|
|
@ -313,6 +347,9 @@ class Plugin extends Model
|
||||||
'config' => $this->configuration ?? [],
|
'config' => $this->configuration ?? [],
|
||||||
...(is_array($this->data_payload) ? $this->data_payload : []),
|
...(is_array($this->data_payload) ? $this->data_payload : []),
|
||||||
'trmnl' => [
|
'trmnl' => [
|
||||||
|
'system' => [
|
||||||
|
'timestamp_utc' => now()->utc()->timestamp,
|
||||||
|
],
|
||||||
'user' => [
|
'user' => [
|
||||||
'utc_offset' => '0',
|
'utc_offset' => '0',
|
||||||
'name' => $this->user->name ?? 'Unknown User',
|
'name' => $this->user->name ?? 'Unknown User',
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class BatteryLow extends Notification
|
||||||
return (new MailMessage)->markdown('mail.battery-low', ['device' => $this->device]);
|
return (new MailMessage)->markdown('mail.battery-low', ['device' => $this->device]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toWebhook(object $notifiable): \App\Notifications\Messages\WebhookMessage
|
public function toWebhook(object $notifiable): WebhookMessage
|
||||||
{
|
{
|
||||||
return WebhookMessage::create()
|
return WebhookMessage::create()
|
||||||
->data([
|
->data([
|
||||||
|
|
|
||||||
|
|
@ -30,3 +30,65 @@ test('days_ago filter with large number works correctly', function (): void {
|
||||||
|
|
||||||
expect($filter->days_ago(100))->toBe($hundredDaysAgo);
|
expect($filter->days_ago(100))->toBe($hundredDaysAgo);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ordinalize filter formats date with ordinal day', function (): void {
|
||||||
|
$filter = new Date();
|
||||||
|
|
||||||
|
expect($filter->ordinalize('2025-10-02', '%A, %B <<ordinal_day>>, %Y'))
|
||||||
|
->toBe('Thursday, October 2nd, 2025');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ordinalize filter handles datetime string with timezone', function (): void {
|
||||||
|
$filter = new Date();
|
||||||
|
|
||||||
|
expect($filter->ordinalize('2025-12-31 16:50:38 -0400', '%A, %b <<ordinal_day>>'))
|
||||||
|
->toBe('Wednesday, Dec 31st');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ordinalize filter handles different ordinal suffixes', function (): void {
|
||||||
|
$filter = new Date();
|
||||||
|
|
||||||
|
// 1st
|
||||||
|
expect($filter->ordinalize('2025-01-01', '<<ordinal_day>>'))
|
||||||
|
->toBe('1st');
|
||||||
|
|
||||||
|
// 2nd
|
||||||
|
expect($filter->ordinalize('2025-01-02', '<<ordinal_day>>'))
|
||||||
|
->toBe('2nd');
|
||||||
|
|
||||||
|
// 3rd
|
||||||
|
expect($filter->ordinalize('2025-01-03', '<<ordinal_day>>'))
|
||||||
|
->toBe('3rd');
|
||||||
|
|
||||||
|
// 4th
|
||||||
|
expect($filter->ordinalize('2025-01-04', '<<ordinal_day>>'))
|
||||||
|
->toBe('4th');
|
||||||
|
|
||||||
|
// 11th (special case)
|
||||||
|
expect($filter->ordinalize('2025-01-11', '<<ordinal_day>>'))
|
||||||
|
->toBe('11th');
|
||||||
|
|
||||||
|
// 12th (special case)
|
||||||
|
expect($filter->ordinalize('2025-01-12', '<<ordinal_day>>'))
|
||||||
|
->toBe('12th');
|
||||||
|
|
||||||
|
// 13th (special case)
|
||||||
|
expect($filter->ordinalize('2025-01-13', '<<ordinal_day>>'))
|
||||||
|
->toBe('13th');
|
||||||
|
|
||||||
|
// 21st
|
||||||
|
expect($filter->ordinalize('2025-01-21', '<<ordinal_day>>'))
|
||||||
|
->toBe('21st');
|
||||||
|
|
||||||
|
// 22nd
|
||||||
|
expect($filter->ordinalize('2025-01-22', '<<ordinal_day>>'))
|
||||||
|
->toBe('22nd');
|
||||||
|
|
||||||
|
// 23rd
|
||||||
|
expect($filter->ordinalize('2025-01-23', '<<ordinal_day>>'))
|
||||||
|
->toBe('23rd');
|
||||||
|
|
||||||
|
// 24th
|
||||||
|
expect($filter->ordinalize('2025-01-24', '<<ordinal_day>>'))
|
||||||
|
->toBe('24th');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue