Add selfserve dynamic image sizing config

This commit is contained in:
Jeppe Bundgaard
2026-06-09 12:46:14 +02:00
parent 6446eb2e36
commit fb1f0883e1
7 changed files with 125 additions and 2 deletions
+21
View File
@@ -19,6 +19,8 @@ use classes\slack as Slack;
use classes\email as Email;
use classes\gatewayapi as GatewayAPI;
use dynamicimages\images\machine_1;
use modules\selfserve\config\selfserve_dynamic_image_size_c;
use modules\selfserve\selfserve_c;
use goals\classes\goals_criteria;
use goals\services\goals_progress_alert_renderer;
use goals\helpers\goals_criteria_progress_alert_destination as Dest;
@@ -33,6 +35,8 @@ use objects\users_o;
use routes\moduleWeatherAPIRoute;
require_once __DIR__ . '/../classes/economic_transfer_executor.php';
const DYNAMIC_IMAGE_RELEVANT_MAX_WIDTH = 1600;
require_once __DIR__ . '/../classes/economic_transfer_queue_schema_bootstrap.php';
require_once __DIR__ . '/../classes/economic_transfer_queue.php';
require_once __DIR__ . '/../classes/workfeed_employee_name_formatter.php';
@@ -930,6 +934,7 @@ function buildDynamicImageCacheKey(array $variant): string
'current_step' => (int)($variant['current_step'] ?? 0),
'only_current_step' => (bool)($variant['only_current_step'] ?? false),
'vehicle_type' => $variant['vehicle_type'] ?? null,
'dynamic_image_size' => getSelfServeDynamicImageSizeModeForCron(),
];
$json = json_encode($cacheParams);
@@ -962,6 +967,9 @@ function renderDynamicImageVariant(int $dynamicImageId, ?array $buttons, int $cu
$image->current_step = max(0, $currentStep);
$image->only_generate_current_step = $onlyCurrentStep;
$image->setup();
if (getSelfServeDynamicImageSizeModeForCron() === selfserve_dynamic_image_size_c::SIZE_RELEVANT) {
$image->resizeToMaxWidth(DYNAMIC_IMAGE_RELEVANT_MAX_WIDTH);
}
$dataUri = $image->exportAsBase64('png');
if (!preg_match('/^data:image\/png;base64,(.*)$/', $dataUri, $matches)) {
@@ -985,6 +993,19 @@ function renderDynamicImageVariant(int $dynamicImageId, ?array $buttons, int $cu
}
}
function getSelfServeDynamicImageSizeModeForCron(): string
{
try {
$mode = (string)(new selfserve_c())->dynamic_image_size->getVariableValue();
} catch (Throwable) {
return selfserve_dynamic_image_size_c::SIZE_ORIGINAL;
}
return in_array($mode, [selfserve_dynamic_image_size_c::SIZE_ORIGINAL, selfserve_dynamic_image_size_c::SIZE_RELEVANT], true)
? $mode
: selfserve_dynamic_image_size_c::SIZE_ORIGINAL;
}
/**
* @param mixed $value
* @return array<int|string>
@@ -226,6 +226,20 @@ trait dynamicimages_image_t
return $this;
}
public function resizeToMaxWidth(int $maxWidth): dynamicimages_image_i
{
$this->assertCanvasInitialized();
if ($maxWidth <= 0) {
throw new \InvalidArgumentException('Resize max width must be a positive integer.');
}
if ($this->canvasWidth === null || $this->canvasHeight === null || $this->canvasWidth <= $maxWidth) {
return $this;
}
$height = (int)round($this->canvasHeight * ($maxWidth / $this->canvasWidth));
return $this->resize($maxWidth, max(1, $height));
}
public function crop(int $width, int $height, int $x, int $y): dynamicimages_image_i
{
$this->assertCanvasInitialized();
@@ -382,4 +396,4 @@ trait dynamicimages_image_t
{
$this->outputImage($format, $quality);
}
}
}
@@ -0,0 +1,32 @@
<?php
namespace modules\selfserve\config;
use Exception;
use traits\module_config_variable;
class selfserve_dynamic_image_size_c
{
use module_config_variable;
public const SIZE_ORIGINAL = 'original';
public const SIZE_RELEVANT = 'relevant';
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'selfserve',
'dynamic_image_size',
'string',
true,
[self::SIZE_ORIGINAL, self::SIZE_RELEVANT],
'Whether self-serve dynamic images are served in the original rendered size or resized to the relevant terminal size',
self::SIZE_RELEVANT,
false,
self::SIZE_ORIGINAL
);
}
}
@@ -4,7 +4,9 @@ namespace modules\selfserve;
require_once WD . '/modules/selfserve/config/selfserve_enabled_c.php';
require_once WD . '/modules/selfserve/config/selfserve_minute_product_c.php';
require_once WD . '/modules/selfserve/config/selfserve_machine_wash_minutes_included_c.php';
require_once WD . '/modules/selfserve/config/selfserve_dynamic_image_size_c.php';
use modules\selfserve\config\selfserve_dynamic_image_size_c;
use modules\selfserve\config\selfserve_enabled_c;
use modules\selfserve\config\selfserve_machine_wash_minutes_included_c;
use modules\selfserve\config\selfserve_minute_product_c;
@@ -29,6 +31,11 @@ class selfserve_c
* @var selfserve_machine_wash_minutes_included_c $machine_wash_minutes_included
*/
public selfserve_machine_wash_minutes_included_c $machine_wash_minutes_included;
/**
* Dynamic image output size mode for self-serve terminals
* @var selfserve_dynamic_image_size_c $dynamic_image_size
*/
public selfserve_dynamic_image_size_c $dynamic_image_size;
public function __construct()
{
@@ -36,10 +43,12 @@ class selfserve_c
$this->allowUpdate([
selfserve_enabled_c::class,
selfserve_minute_product_c::class,
selfserve_machine_wash_minutes_included_c::class
selfserve_machine_wash_minutes_included_c::class,
selfserve_dynamic_image_size_c::class
]);
$this->enabled = new selfserve_enabled_c();
$this->minute_product = new selfserve_minute_product_c();
$this->machine_wash_minutes_included = new selfserve_machine_wash_minutes_included_c();
$this->dynamic_image_size = new selfserve_dynamic_image_size_c();
}
}
@@ -6,6 +6,8 @@ use classes\authentication;
use classes\response;
use classes\shelly_relay_inventory;
use dynamicimages\images\machine_1;
use modules\selfserve\config\selfserve_dynamic_image_size_c;
use modules\selfserve\selfserve_c;
use objects\categories_o;
use objects\department_lanes_o;
use objects\department_selfserve_tasks_o;
@@ -16,6 +18,8 @@ class departmentLanesRoute
{
use route_t;
private const RELEVANT_DYNAMIC_IMAGE_MAX_WIDTH = 1600;
public function run(): void
{
$this->get('/department/lanes/status-toggles', function () {
@@ -219,6 +223,7 @@ class departmentLanesRoute
$response->error('No dynamic image configured for this lane', 404);
}
$dynamic_image_id = (int)$dynamic_image_id;
$dynamic_image_size = self::getSelfServeDynamicImageSizeMode();
// Parse optional params
$buttons = null;
@@ -278,6 +283,7 @@ class departmentLanesRoute
'department' => $department_id,
'lane' => $lane_id,
'dynamic_image_id' => $dynamic_image_id,
'dynamic_image_size' => $dynamic_image_size,
];
$cacheKey = 'dynamic_image:' . md5(json_encode($cacheParams));
$cachedImage = redis->get($cacheKey);
@@ -313,6 +319,9 @@ class departmentLanesRoute
// Compose and serve the image
try {
$image->setup();
if ($dynamic_image_size === selfserve_dynamic_image_size_c::SIZE_RELEVANT) {
$image->resizeToMaxWidth(self::RELEVANT_DYNAMIC_IMAGE_MAX_WIDTH);
}
// If caching is enabled, we need to capture the output or use export
if ($cacheKey && defined('redis')) {
@@ -527,4 +536,17 @@ class departmentLanesRoute
$field->set($normalized);
}
private static function getSelfServeDynamicImageSizeMode(): string
{
try {
$mode = (string)(new selfserve_c())->dynamic_image_size->getVariableValue();
} catch (\Throwable) {
return selfserve_dynamic_image_size_c::SIZE_ORIGINAL;
}
return in_array($mode, [selfserve_dynamic_image_size_c::SIZE_ORIGINAL, selfserve_dynamic_image_size_c::SIZE_RELEVANT], true)
? $mode
: selfserve_dynamic_image_size_c::SIZE_ORIGINAL;
}
}
@@ -38,6 +38,28 @@ namespace {
expect($content)->toContain("switch (\$dynamic_image_id)");
});
it('uses the selfserve dynamic image size config for route output and cache keys', function (): void {
$route = file_get_contents(app_path('routes/departmentLanesRoute.php'));
$config = file_get_contents(app_path('modules/selfserve/config/selfserve_dynamic_image_size_c.php'));
$moduleConfig = file_get_contents(app_path('modules/selfserve/selfserve_c.php'));
$imageTrait = file_get_contents(app_path('modules/dynamicimages/traits/dynamicimages_image_t.php'));
expect($route)->not->toBeFalse();
expect($config)->not->toBeFalse();
expect($moduleConfig)->not->toBeFalse();
expect($imageTrait)->not->toBeFalse();
expect($config)->toContain("self::SIZE_ORIGINAL");
expect($config)->toContain("self::SIZE_RELEVANT");
expect($config)->toContain("[self::SIZE_ORIGINAL, self::SIZE_RELEVANT]");
expect($config)->toContain("'dynamic_image_size'");
expect($moduleConfig)->toContain("selfserve_dynamic_image_size_c::class");
expect($moduleConfig)->toContain('public selfserve_dynamic_image_size_c $dynamic_image_size;');
expect($route)->toContain('$dynamic_image_size = self::getSelfServeDynamicImageSizeMode();');
expect($route)->toContain("'dynamic_image_size' => \$dynamic_image_size");
expect($route)->toContain("resizeToMaxWidth(self::RELEVANT_DYNAMIC_IMAGE_MAX_WIDTH)");
expect($imageTrait)->toContain('function resizeToMaxWidth(int $maxWidth)');
});
it('accepts ordered dynamic image button tokens including program picker reset start and zero', function (): void {
expect(department_selfserve_tasks_o::normalizeButtonsInput('["program_picker","reset",0,2,"start",5]'))->toBe([
'program_picker',
@@ -13,4 +13,7 @@ it('registers dynamic image pre-render cron task and related helpers', function
expect($content)->toContain('normalizeButtonsInput');
expect($content)->toContain('dynamic_image:');
expect($content)->toContain('machine_1');
expect($content)->toContain('getSelfServeDynamicImageSizeModeForCron');
expect($content)->toContain("'dynamic_image_size' => getSelfServeDynamicImageSizeModeForCron()");
expect($content)->toContain('resizeToMaxWidth(DYNAMIC_IMAGE_RELEVANT_MAX_WIDTH)');
});