Store: enforce configurable timezone for store timestamps
This commit is contained in:
@@ -19,6 +19,7 @@ class StoreController
|
|||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
$this->applyStoreTimezone();
|
||||||
$this->view = new View(__DIR__ . '/views');
|
$this->view = new View(__DIR__ . '/views');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +172,8 @@ class StoreController
|
|||||||
$expiryDays = max(1, (int)$expiryDaysRaw);
|
$expiryDays = max(1, (int)$expiryDaysRaw);
|
||||||
$orderPrefixRaw = array_key_exists('store_order_prefix', $_POST) ? (string)$_POST['store_order_prefix'] : (string)($current['store_order_prefix'] ?? 'AC-ORD');
|
$orderPrefixRaw = array_key_exists('store_order_prefix', $_POST) ? (string)$_POST['store_order_prefix'] : (string)($current['store_order_prefix'] ?? 'AC-ORD');
|
||||||
$orderPrefix = $this->sanitizeOrderPrefix($orderPrefixRaw);
|
$orderPrefix = $this->sanitizeOrderPrefix($orderPrefixRaw);
|
||||||
|
$timezoneRaw = array_key_exists('store_timezone', $_POST) ? (string)$_POST['store_timezone'] : (string)($current['store_timezone'] ?? 'UTC');
|
||||||
|
$timezone = $this->normalizeTimezone($timezoneRaw);
|
||||||
$testMode = array_key_exists('store_test_mode', $_POST) ? ((string)$_POST['store_test_mode'] === '1' ? '1' : '0') : (string)($current['store_test_mode'] ?? '1');
|
$testMode = array_key_exists('store_test_mode', $_POST) ? ((string)$_POST['store_test_mode'] === '1' ? '1' : '0') : (string)($current['store_test_mode'] ?? '1');
|
||||||
$stripeEnabled = array_key_exists('store_stripe_enabled', $_POST) ? ((string)$_POST['store_stripe_enabled'] === '1' ? '1' : '0') : (string)($current['store_stripe_enabled'] ?? '0');
|
$stripeEnabled = array_key_exists('store_stripe_enabled', $_POST) ? ((string)$_POST['store_stripe_enabled'] === '1' ? '1' : '0') : (string)($current['store_stripe_enabled'] ?? '0');
|
||||||
$stripePublic = trim((string)(array_key_exists('store_stripe_public_key', $_POST) ? $_POST['store_stripe_public_key'] : ($current['store_stripe_public_key'] ?? '')));
|
$stripePublic = trim((string)(array_key_exists('store_stripe_public_key', $_POST) ? $_POST['store_stripe_public_key'] : ($current['store_stripe_public_key'] ?? '')));
|
||||||
@@ -201,6 +204,7 @@ class StoreController
|
|||||||
Settings::set('store_download_limit', (string)$downloadLimit);
|
Settings::set('store_download_limit', (string)$downloadLimit);
|
||||||
Settings::set('store_download_expiry_days', (string)$expiryDays);
|
Settings::set('store_download_expiry_days', (string)$expiryDays);
|
||||||
Settings::set('store_order_prefix', $orderPrefix);
|
Settings::set('store_order_prefix', $orderPrefix);
|
||||||
|
Settings::set('store_timezone', $timezone);
|
||||||
Settings::set('store_test_mode', $testMode);
|
Settings::set('store_test_mode', $testMode);
|
||||||
Settings::set('store_stripe_enabled', $stripeEnabled);
|
Settings::set('store_stripe_enabled', $stripeEnabled);
|
||||||
Settings::set('store_stripe_public_key', $stripePublic);
|
Settings::set('store_stripe_public_key', $stripePublic);
|
||||||
@@ -2257,6 +2261,7 @@ class StoreController
|
|||||||
'store_download_limit' => Settings::get('store_download_limit', '5'),
|
'store_download_limit' => Settings::get('store_download_limit', '5'),
|
||||||
'store_download_expiry_days' => Settings::get('store_download_expiry_days', '30'),
|
'store_download_expiry_days' => Settings::get('store_download_expiry_days', '30'),
|
||||||
'store_order_prefix' => Settings::get('store_order_prefix', 'AC-ORD'),
|
'store_order_prefix' => Settings::get('store_order_prefix', 'AC-ORD'),
|
||||||
|
'store_timezone' => Settings::get('store_timezone', 'UTC'),
|
||||||
'store_test_mode' => Settings::get('store_test_mode', '1'),
|
'store_test_mode' => Settings::get('store_test_mode', '1'),
|
||||||
'store_stripe_enabled' => Settings::get('store_stripe_enabled', '0'),
|
'store_stripe_enabled' => Settings::get('store_stripe_enabled', '0'),
|
||||||
'store_stripe_public_key' => Settings::get('store_stripe_public_key', ''),
|
'store_stripe_public_key' => Settings::get('store_stripe_public_key', ''),
|
||||||
@@ -2276,6 +2281,39 @@ class StoreController
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeTimezone(string $timezone): string
|
||||||
|
{
|
||||||
|
$timezone = trim($timezone);
|
||||||
|
if ($timezone === '') {
|
||||||
|
return 'UTC';
|
||||||
|
}
|
||||||
|
return in_array($timezone, \DateTimeZone::listIdentifiers(), true) ? $timezone : 'UTC';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyStoreTimezone(): void
|
||||||
|
{
|
||||||
|
$timezone = $this->normalizeTimezone((string)Settings::get('store_timezone', 'UTC'));
|
||||||
|
@date_default_timezone_set($timezone);
|
||||||
|
|
||||||
|
$db = Database::get();
|
||||||
|
if (!($db instanceof PDO)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tz = new \DateTimeZone($timezone);
|
||||||
|
$now = new \DateTimeImmutable('now', $tz);
|
||||||
|
$offset = $tz->getOffset($now);
|
||||||
|
$sign = $offset < 0 ? '-' : '+';
|
||||||
|
$offset = abs($offset);
|
||||||
|
$hours = str_pad((string)intdiv($offset, 3600), 2, '0', STR_PAD_LEFT);
|
||||||
|
$mins = str_pad((string)intdiv($offset % 3600, 60), 2, '0', STR_PAD_LEFT);
|
||||||
|
$dbTz = $sign . $hours . ':' . $mins;
|
||||||
|
$db->exec("SET time_zone = '" . $dbTz . "'");
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function ensureSalesChartSchema(): void
|
private function ensureSalesChartSchema(): void
|
||||||
{
|
{
|
||||||
$db = Database::get();
|
$db = Database::get();
|
||||||
|
|||||||
@@ -69,6 +69,22 @@ ob_start();
|
|||||||
|
|
||||||
<div class="label" style="margin-top:12px;">Order Number Prefix</div>
|
<div class="label" style="margin-top:12px;">Order Number Prefix</div>
|
||||||
<input class="input" name="store_order_prefix" value="<?= htmlspecialchars((string)($settings['store_order_prefix'] ?? 'AC-ORD'), ENT_QUOTES, 'UTF-8') ?>" placeholder="AC-ORD">
|
<input class="input" name="store_order_prefix" value="<?= htmlspecialchars((string)($settings['store_order_prefix'] ?? 'AC-ORD'), ENT_QUOTES, 'UTF-8') ?>" placeholder="AC-ORD">
|
||||||
|
|
||||||
|
<div class="label" style="margin-top:12px;">Store Timezone</div>
|
||||||
|
<input class="input" name="store_timezone" list="store-timezone-options" value="<?= htmlspecialchars((string)($settings['store_timezone'] ?? 'UTC'), ENT_QUOTES, 'UTF-8') ?>" placeholder="UTC">
|
||||||
|
<datalist id="store-timezone-options">
|
||||||
|
<option value="UTC"></option>
|
||||||
|
<option value="Europe/London"></option>
|
||||||
|
<option value="Europe/Berlin"></option>
|
||||||
|
<option value="America/New_York"></option>
|
||||||
|
<option value="America/Chicago"></option>
|
||||||
|
<option value="America/Los_Angeles"></option>
|
||||||
|
<option value="Australia/Sydney"></option>
|
||||||
|
<option value="Asia/Tokyo"></option>
|
||||||
|
</datalist>
|
||||||
|
<div style="margin-top:8px; font-size:12px; color:var(--muted);">
|
||||||
|
Used for order numbers, store timestamps, and expiry calculations. Invalid values fall back to UTC.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display:flex; justify-content:flex-end;">
|
<div style="display:flex; justify-content:flex-end;">
|
||||||
|
|||||||
Reference in New Issue
Block a user