mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
feat: add trmnl-liquid renderer option
This commit is contained in:
parent
a7a541da42
commit
aa80e944f9
3 changed files with 135 additions and 51 deletions
|
|
@ -19,6 +19,7 @@ use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Blade;
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Keepsuit\LaravelLiquid\LaravelLiquidExtension;
|
use Keepsuit\LaravelLiquid\LaravelLiquidExtension;
|
||||||
use Keepsuit\Liquid\Exceptions\LiquidException;
|
use Keepsuit\Liquid\Exceptions\LiquidException;
|
||||||
|
|
@ -40,6 +41,7 @@ class Plugin extends Model
|
||||||
'configuration_template' => 'json',
|
'configuration_template' => 'json',
|
||||||
'no_bleed' => 'boolean',
|
'no_bleed' => 'boolean',
|
||||||
'dark_mode' => 'boolean',
|
'dark_mode' => 'boolean',
|
||||||
|
'preferred_renderer' => 'string',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static function boot()
|
protected static function boot()
|
||||||
|
|
@ -363,6 +365,50 @@ class Plugin extends Model
|
||||||
return $liquidTemplate->render($context);
|
return $liquidTemplate->render($context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render template using external Ruby liquid renderer
|
||||||
|
*
|
||||||
|
* @param string $template The liquid template string
|
||||||
|
* @param array $context The render context data
|
||||||
|
* @return string The rendered HTML
|
||||||
|
*
|
||||||
|
* @throws LiquidException
|
||||||
|
*/
|
||||||
|
private function renderWithExternalLiquidRenderer(string $template, array $context): string
|
||||||
|
{
|
||||||
|
$liquidPath = config('services.trmnl.liquid_path');
|
||||||
|
|
||||||
|
if (empty($liquidPath)) {
|
||||||
|
throw new LiquidException('External liquid renderer path is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML encode the template
|
||||||
|
$encodedTemplate = htmlspecialchars($template, ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
|
// Encode context as JSON
|
||||||
|
$jsonContext = json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
if ($jsonContext === false) {
|
||||||
|
throw new LiquidException('Failed to encode render context as JSON: '.json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the external renderer
|
||||||
|
$process = Process::run([
|
||||||
|
$liquidPath,
|
||||||
|
'--template',
|
||||||
|
$encodedTemplate,
|
||||||
|
'--context',
|
||||||
|
$jsonContext,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! $process->successful()) {
|
||||||
|
$errorOutput = $process->errorOutput() ?: $process->output();
|
||||||
|
throw new Exception('External liquid renderer failed: '.$errorOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $process->output();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the plugin's markup
|
* Render the plugin's markup
|
||||||
*
|
*
|
||||||
|
|
@ -374,59 +420,67 @@ class Plugin extends Model
|
||||||
$renderedContent = '';
|
$renderedContent = '';
|
||||||
|
|
||||||
if ($this->markup_language === 'liquid') {
|
if ($this->markup_language === 'liquid') {
|
||||||
// Create a custom environment with inline templates support
|
// Build render context
|
||||||
$inlineFileSystem = new InlineTemplatesFileSystem();
|
$context = [
|
||||||
$environment = new \Keepsuit\Liquid\Environment(
|
'size' => $size,
|
||||||
fileSystem: $inlineFileSystem,
|
'data' => $this->data_payload,
|
||||||
extensions: [new StandardExtension(), new LaravelLiquidExtension()]
|
'config' => $this->configuration ?? [],
|
||||||
);
|
...(is_array($this->data_payload) ? $this->data_payload : []),
|
||||||
|
'trmnl' => [
|
||||||
// Register all custom filters
|
'system' => [
|
||||||
$environment->filterRegistry->register(Data::class);
|
'timestamp_utc' => now()->utc()->timestamp,
|
||||||
$environment->filterRegistry->register(Date::class);
|
],
|
||||||
$environment->filterRegistry->register(Localization::class);
|
'user' => [
|
||||||
$environment->filterRegistry->register(Numbers::class);
|
'utc_offset' => '0',
|
||||||
$environment->filterRegistry->register(StringMarkup::class);
|
'name' => $this->user->name ?? 'Unknown User',
|
||||||
$environment->filterRegistry->register(Uniqueness::class);
|
'locale' => 'en',
|
||||||
|
'time_zone_iana' => config('app.timezone'),
|
||||||
// Register the template tag for inline templates
|
],
|
||||||
$environment->tagRegistry->register(TemplateTag::class);
|
'plugin_settings' => [
|
||||||
|
'instance_name' => $this->name,
|
||||||
// Apply Liquid replacements (including 'with' syntax conversion)
|
'strategy' => $this->data_strategy,
|
||||||
$processedMarkup = $this->applyLiquidReplacements($this->render_markup);
|
'dark_mode' => $this->dark_mode ? 'yes' : 'no',
|
||||||
|
'no_screen_padding' => $this->no_bleed ? 'yes' : 'no',
|
||||||
$template = $environment->parseString($processedMarkup);
|
'polling_headers' => $this->polling_header,
|
||||||
$context = $environment->newRenderContext(
|
'polling_url' => $this->polling_url,
|
||||||
data: [
|
'custom_fields_values' => [
|
||||||
'size' => $size,
|
...(is_array($this->configuration) ? $this->configuration : []),
|
||||||
'data' => $this->data_payload,
|
|
||||||
'config' => $this->configuration ?? [],
|
|
||||||
...(is_array($this->data_payload) ? $this->data_payload : []),
|
|
||||||
'trmnl' => [
|
|
||||||
'system' => [
|
|
||||||
'timestamp_utc' => now()->utc()->timestamp,
|
|
||||||
],
|
|
||||||
'user' => [
|
|
||||||
'utc_offset' => '0',
|
|
||||||
'name' => $this->user->name ?? 'Unknown User',
|
|
||||||
'locale' => 'en',
|
|
||||||
'time_zone_iana' => config('app.timezone'),
|
|
||||||
],
|
|
||||||
'plugin_settings' => [
|
|
||||||
'instance_name' => $this->name,
|
|
||||||
'strategy' => $this->data_strategy,
|
|
||||||
'dark_mode' => $this->dark_mode ? 'yes' : 'no',
|
|
||||||
'no_screen_padding' => $this->no_bleed ? 'yes' : 'no',
|
|
||||||
'polling_headers' => $this->polling_header,
|
|
||||||
'polling_url' => $this->polling_url,
|
|
||||||
'custom_fields_values' => [
|
|
||||||
...(is_array($this->configuration) ? $this->configuration : []),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
);
|
];
|
||||||
$renderedContent = $template->render($context);
|
|
||||||
|
// Check if external renderer should be used
|
||||||
|
if ($this->preferred_renderer === 'trmnl-liquid' && config('services.trmnl.liquid_enabled')) {
|
||||||
|
// Use external Ruby renderer - pass raw template without preprocessing
|
||||||
|
$renderedContent = $this->renderWithExternalLiquidRenderer($this->render_markup, $context);
|
||||||
|
} else {
|
||||||
|
// Use PHP keepsuit/liquid renderer
|
||||||
|
// Create a custom environment with inline templates support
|
||||||
|
$inlineFileSystem = new InlineTemplatesFileSystem();
|
||||||
|
$environment = new \Keepsuit\Liquid\Environment(
|
||||||
|
fileSystem: $inlineFileSystem,
|
||||||
|
extensions: [new StandardExtension(), new LaravelLiquidExtension()]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register all custom filters
|
||||||
|
$environment->filterRegistry->register(Data::class);
|
||||||
|
$environment->filterRegistry->register(Date::class);
|
||||||
|
$environment->filterRegistry->register(Localization::class);
|
||||||
|
$environment->filterRegistry->register(Numbers::class);
|
||||||
|
$environment->filterRegistry->register(StringMarkup::class);
|
||||||
|
$environment->filterRegistry->register(Uniqueness::class);
|
||||||
|
|
||||||
|
// Register the template tag for inline templates
|
||||||
|
$environment->tagRegistry->register(TemplateTag::class);
|
||||||
|
|
||||||
|
// Apply Liquid replacements (including 'with' syntax conversion)
|
||||||
|
$processedMarkup = $this->applyLiquidReplacements($this->render_markup);
|
||||||
|
|
||||||
|
$template = $environment->parseString($processedMarkup);
|
||||||
|
$liquidContext = $environment->newRenderContext(data: $context);
|
||||||
|
$renderedContent = $template->render($liquidContext);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$renderedContent = Blade::render($this->render_markup, [
|
$renderedContent = Blade::render($this->render_markup, [
|
||||||
'size' => $size,
|
'size' => $size,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ return [
|
||||||
'proxy_refresh_cron' => env('TRMNL_PROXY_REFRESH_CRON'),
|
'proxy_refresh_cron' => env('TRMNL_PROXY_REFRESH_CRON'),
|
||||||
'override_orig_icon' => env('TRMNL_OVERRIDE_ORIG_ICON', false),
|
'override_orig_icon' => env('TRMNL_OVERRIDE_ORIG_ICON', false),
|
||||||
'image_url_timeout' => env('TRMNL_IMAGE_URL_TIMEOUT', 30), // 30 seconds; increase on low-powered devices
|
'image_url_timeout' => env('TRMNL_IMAGE_URL_TIMEOUT', 30), // 30 seconds; increase on low-powered devices
|
||||||
|
'liquid_enabled' => env('TRMNL_LIQUID_ENABLED', false),
|
||||||
|
'liquid_path' => env('TRMNL_LIQUID_PATH', '/usr/local/bin/trmnl-liquid-cli'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'webhook' => [
|
'webhook' => [
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('plugins', function (Blueprint $table) {
|
||||||
|
$table->string('preferred_renderer')->nullable()->after('markup_language');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('plugins', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('preferred_renderer');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue