Compare commits

..

2 commits

Author SHA1 Message Date
Benjamin Nussbaum
b96a96155d chore: format fixes
Some checks failed
tests / ci (push) Has been cancelled
2026-02-07 14:44:20 +01:00
Benjamin Nussbaum
a37a9cfe96 fix: shared template context injection 2026-02-07 14:44:20 +01:00
5 changed files with 85 additions and 5 deletions

View file

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Liquid\Tags;
use Keepsuit\Liquid\Render\RenderContext;
use Keepsuit\Liquid\Support\MissingValue;
use Keepsuit\Liquid\Tags\RenderTag;
/**
* Render tag that injects plugin context (trmnl, size, data, config) into partials
* so shared templates can use variables like trmnl.user.name without passing them explicitly.
*/
class PluginRenderTag extends RenderTag
{
/**
* Root-level keys from the plugin render context that should be available in partials.
*
* @var list<string>
*/
private const PARENT_CONTEXT_KEYS = ['trmnl', 'size', 'data', 'config'];
protected function buildPartialContext(RenderContext $rootContext, string $templateName, array $variables = []): RenderContext
{
$partialContext = $rootContext->newIsolatedSubContext($templateName);
foreach (self::PARENT_CONTEXT_KEYS as $key) {
$value = $rootContext->get($key);
if ($value !== null && ! $value instanceof MissingValue) {
$partialContext->set($key, $value);
}
}
foreach ($variables as $key => $value) {
$partialContext->set($key, $value);
}
foreach ($this->attributes as $key => $value) {
$partialContext->set($key, $rootContext->evaluate($value));
}
return $partialContext;
}
}

View file

@ -10,6 +10,7 @@ use App\Liquid\Filters\Numbers;
use App\Liquid\Filters\StandardFilters;
use App\Liquid\Filters\StringMarkup;
use App\Liquid\Filters\Uniqueness;
use App\Liquid\Tags\PluginRenderTag;
use App\Liquid\Tags\TemplateTag;
use App\Services\Plugin\Parsers\ResponseParserRegistry;
use App\Services\PluginImportService;
@ -499,6 +500,8 @@ class Plugin extends Model
// Register the template tag for inline templates
$environment->tagRegistry->register(TemplateTag::class);
// Use plugin render tag so partials receive trmnl, size, data, config
$environment->tagRegistry->register(PluginRenderTag::class);
// Apply Liquid replacements (including 'with' syntax conversion)
$processedMarkup = $this->applyLiquidReplacements($markup);

View file

@ -8,15 +8,15 @@ return new class extends Migration
{
public function up(): void
{
Schema::table("devices", function (Blueprint $table): void {
$table->boolean("maximum_compatibility")->default(false);
Schema::table('devices', function (Blueprint $table): void {
$table->boolean('maximum_compatibility')->default(false);
});
}
public function down(): void
{
Schema::table("devices", function (Blueprint $table): void {
$table->dropColumn("maximum_compatibility");
Schema::table('devices', function (Blueprint $table): void {
$table->dropColumn('maximum_compatibility');
});
}
};

View file

@ -100,7 +100,7 @@ test('display endpoint includes maximum_compatibility value when true for device
$device = Device::factory()->create([
'mac_address' => '00:11:22:33:44:55',
'api_key' => 'test-api-key',
'maximum_compatibility' => true
'maximum_compatibility' => true,
]);
$response = $this->withHeaders([

View file

@ -1,6 +1,7 @@
<?php
use App\Models\Plugin;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
@ -238,3 +239,34 @@ LIQUID
$this->assertStringContainsString('"35":[{"name":"Ryan","age":35}]', $result);
$this->assertStringContainsString('"29":[{"name":"Sara","age":29},{"name":"Jimbob","age":29}]', $result);
});
test('shared template receives trmnl context when', function (): void {
$user = User::factory()->create(['name' => 'Jane Smith']);
$plugin = Plugin::factory()->create([
'user_id' => $user->id,
'name' => 'Departures',
'markup_language' => 'liquid',
'render_markup_shared' => <<<'LIQUID'
{% template departures_view %}
<div class="title_bar">
<span class="title">Departures</span>
<span class="instance">{{ trmnl.user.name }}</span>
</div>
{% endtemplate %}
LIQUID
,
'render_markup' => <<<'LIQUID'
<div class="view">
{% render "departures_view", station: "Hauptbahnhof" %}
</div>
LIQUID
,
'data_payload' => [],
]);
$result = $plugin->render('full');
$this->assertStringContainsString('Jane Smith', $result);
$this->assertStringContainsString('class="instance"', $result);
});