feat: Add FaceAI integration with handoff and return functionality
- Introduced a new workspace for FaceAI in package.json. - Implemented FaceAI handoff logic in faceai_handoff.php, including identity verification and token signing. - Created faceai_return.php to handle return requests from FaceAI, validating tokens and forwarding results. - Developed faceai_simulator.php and faceai_simulator_view.php for simulating the FaceAI interface with demo photos. - Enhanced rus-ecom-240621.js to support new FaceAI features, including dynamic URL building and button integration. - Added faceai_config.php for configuration management, including environment variable handling and utility functions. - Updated HTML structure and styles in simulator view for better user experience.
This commit is contained in:
parent
f65a85dcc9
commit
da362c201f
31 changed files with 4511 additions and 60 deletions
|
|
@ -96,10 +96,89 @@ function searchGara() {
|
|||
/* PAGINA RICERCA FOTOCR */
|
||||
/***************************************************/
|
||||
/***************************************************/
|
||||
function getTipoPuntoFotoValue() {
|
||||
var field = $("#tipoPuntoFoto");
|
||||
if (!field.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return field.val() || "";
|
||||
}
|
||||
|
||||
function getCurrentLangValue() {
|
||||
var field = $("#lang");
|
||||
if (field.length && field.val()) {
|
||||
return field.val();
|
||||
}
|
||||
|
||||
return $("html").attr("lang") || "it";
|
||||
}
|
||||
|
||||
function buildFaceAiLaunchUrl() {
|
||||
var raceId = $("#id_gara").val() || "";
|
||||
var raceSlug = $("#garaDesc").val() || "";
|
||||
var raceName = $("h1.my-4").last().text().replace(/\s+/g, " ").trim();
|
||||
var lang = getCurrentLangValue();
|
||||
var handoffUrl = (window.faceAiSimulator && window.faceAiSimulator.handoffUrl) || "faceai_handoff.php";
|
||||
var returnUrl = (window.faceAiSimulator && window.faceAiSimulator.returnUrl) || window.location.href;
|
||||
var query = [
|
||||
"raceId=" + encodeURIComponent(raceId),
|
||||
"raceSlug=" + encodeURIComponent(raceSlug),
|
||||
"raceName=" + encodeURIComponent(raceName),
|
||||
"lang=" + encodeURIComponent(lang),
|
||||
"returnUrl=" + encodeURIComponent(returnUrl)
|
||||
];
|
||||
|
||||
if (window.faceAiSimulator && window.faceAiSimulator.devUserId) {
|
||||
query.push("devUserId=" + encodeURIComponent(window.faceAiSimulator.devUserId));
|
||||
}
|
||||
if (window.faceAiSimulator && window.faceAiSimulator.devDisplayName) {
|
||||
query.push("devDisplayName=" + encodeURIComponent(window.faceAiSimulator.devDisplayName));
|
||||
}
|
||||
if (window.faceAiSimulator && window.faceAiSimulator.devEmail) {
|
||||
query.push("devEmail=" + encodeURIComponent(window.faceAiSimulator.devEmail));
|
||||
}
|
||||
if (window.faceAiSimulator && window.faceAiSimulator.devMembershipStatus) {
|
||||
query.push("devMembershipStatus=" + encodeURIComponent(window.faceAiSimulator.devMembershipStatus));
|
||||
}
|
||||
|
||||
return handoffUrl + "?" + query.join("&");
|
||||
}
|
||||
|
||||
function launchFaceAi() {
|
||||
$("body").addClass("loading");
|
||||
window.location.href = buildFaceAiLaunchUrl();
|
||||
return false;
|
||||
}
|
||||
|
||||
function initFaceAiRaceSearchButton() {
|
||||
var select = $("#tipoPuntoFoto");
|
||||
if (!select.length || $("#faceaiLaunchButton").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var inputGroup = select.closest(".input-group");
|
||||
var renderTarget = inputGroup.length ? inputGroup : select.parent();
|
||||
var currentValue = select.val() || "";
|
||||
|
||||
if (!renderTarget.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
select.off("change");
|
||||
select.remove();
|
||||
|
||||
if (!$("#tipoPuntoFoto").length) {
|
||||
renderTarget.append('<input type="hidden" name="tipoPuntoFoto" id="tipoPuntoFoto" value="' + currentValue.replace(/"/g, '"') + '">');
|
||||
}
|
||||
|
||||
renderTarget.append('<button type="button" id="faceaiLaunchButton" class="btn btn-warning btn-block text-uppercase" onclick="return launchFaceAi();"><i class="fa fa-camera-retro" aria-hidden="true"></i> Face ID</button>');
|
||||
}
|
||||
|
||||
function searching() {
|
||||
//gara%201_gara-1---2.html
|
||||
$("body").addClass("loading");
|
||||
theSvlt = $("#garaDesc").val() + "_gara-" + $("#id_gara").val() + "-" + $("#id_puntoFoto").val() + "-" + $("#tipoPuntoFoto").val() + "-" + $("#pageRow").val() + "-1-"+$("#pettorale").val()+"-"+$("#lang").val()+".html";
|
||||
theSvlt = $("#garaDesc").val() + "_gara-" + $("#id_gara").val() + "-" + $("#id_puntoFoto").val() + "-" + getTipoPuntoFotoValue() + "-" + $("#pageRow").val() + "-1-"+$("#pettorale").val()+"-"+getCurrentLangValue()+".html";
|
||||
//alert(theSvlt);
|
||||
location.href = theSvlt;
|
||||
|
||||
|
|
@ -107,7 +186,7 @@ function searching() {
|
|||
function searchingTPF() {
|
||||
//gara%201_gara-1---2.html
|
||||
|
||||
theSvlt = $("#garaDesc").val() + "_gara-" + $("#id_gara").val() + "--" + $("#tipoPuntoFoto").val() + "-" + $("#pageRow").val() + "-1.html";
|
||||
theSvlt = $("#garaDesc").val() + "_gara-" + $("#id_gara").val() + "--" + getTipoPuntoFotoValue() + "-" + $("#pageRow").val() + "-1.html";
|
||||
//alert(theSvlt);
|
||||
location.href = theSvlt;
|
||||
|
||||
|
|
@ -288,7 +367,7 @@ function goPage()
|
|||
|
||||
if(parseFloat(pnGo)<= parseFloat(pn))
|
||||
{
|
||||
theSvlt = $("#garaDesc").val() + "_gara-" + $("#id_gara").val() + "-" + $("#id_puntoFoto").val() + "-" + $("#tipoPuntoFoto").val() + "-" + $("#pageRow").val() + "-"+pnGo+".html";
|
||||
theSvlt = $("#garaDesc").val() + "_gara-" + $("#id_gara").val() + "-" + $("#id_puntoFoto").val() + "-" + getTipoPuntoFotoValue() + "-" + $("#pageRow").val() + "-"+pnGo+".html";
|
||||
//alert(theSvlt);
|
||||
location.href = theSvlt;
|
||||
}
|
||||
|
|
@ -296,6 +375,10 @@ function goPage()
|
|||
alert('Errore!!');
|
||||
}
|
||||
|
||||
$(function() {
|
||||
initFaceAiRaceSearchButton();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
185
www/faceai_config.php
Normal file
185
www/faceai_config.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
function faceai_env($key, $default = null)
|
||||
{
|
||||
$value = getenv($key);
|
||||
return $value === false ? $default : $value;
|
||||
}
|
||||
|
||||
function faceai_config()
|
||||
{
|
||||
static $config = null;
|
||||
|
||||
if ($config !== null) {
|
||||
return $config;
|
||||
}
|
||||
|
||||
$config = array(
|
||||
'frontend_url' => rtrim(faceai_env('FACEAI_FRONTEND_URL', 'http://localhost:5173'), '/'),
|
||||
'backend_internal_url' => rtrim(faceai_env('FACEAI_BACKEND_INTERNAL_URL', 'http://localhost:3001'), '/'),
|
||||
'shared_secret' => (string) faceai_env('FACEAI_SHARED_SECRET', 'change-me'),
|
||||
'allow_dev_handoff' => faceai_env('FACEAI_ALLOW_DEV_HANDOFF', '1') === '1',
|
||||
'identity_cookie' => (string) faceai_env('FACEAI_IDENTITY_COOKIE', 'rus_faceai_identity'),
|
||||
'return_forward_url' => rtrim((string) faceai_env('FACEAI_RETURN_FORWARD_URL', ''), '/')
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
function faceai_base64url_encode($value)
|
||||
{
|
||||
return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
function faceai_base64url_decode($value)
|
||||
{
|
||||
$padding = strlen($value) % 4;
|
||||
if ($padding > 0) {
|
||||
$value .= str_repeat('=', 4 - $padding);
|
||||
}
|
||||
|
||||
return base64_decode(strtr($value, '-_', '+/'));
|
||||
}
|
||||
|
||||
function faceai_sign_payload(array $payload, $secret)
|
||||
{
|
||||
$body = faceai_base64url_encode(json_encode($payload));
|
||||
$signature = hash_hmac('sha256', $body, $secret, true);
|
||||
return $body . '.' . faceai_base64url_encode($signature);
|
||||
}
|
||||
|
||||
function faceai_verify_payload($token, $secret)
|
||||
{
|
||||
if (!is_string($token) || strpos($token, '.') === false) {
|
||||
throw new RuntimeException('Invalid token format.');
|
||||
}
|
||||
|
||||
list($body, $signature) = explode('.', $token, 2);
|
||||
$expected = faceai_base64url_encode(hash_hmac('sha256', $body, $secret, true));
|
||||
|
||||
if (!hash_equals($expected, $signature)) {
|
||||
throw new RuntimeException('Invalid token signature.');
|
||||
}
|
||||
|
||||
$decoded = faceai_base64url_decode($body);
|
||||
$payload = json_decode($decoded, true);
|
||||
|
||||
if (!is_array($payload)) {
|
||||
throw new RuntimeException('Invalid token payload.');
|
||||
}
|
||||
|
||||
if (isset($payload['expiresAt']) && (int) $payload['expiresAt'] < (int) round(microtime(true) * 1000)) {
|
||||
throw new RuntimeException('Token expired.');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
function faceai_build_url($baseUrl, array $params)
|
||||
{
|
||||
return $baseUrl . (strpos($baseUrl, '?') === false ? '?' : '&') . http_build_query($params);
|
||||
}
|
||||
|
||||
function faceai_request_value($key, $default = '')
|
||||
{
|
||||
if (!isset($_GET[$key])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (is_array($_GET[$key])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return trim((string) $_GET[$key]);
|
||||
}
|
||||
|
||||
function faceai_html($value)
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function faceai_resolve_identity(array $config)
|
||||
{
|
||||
if (!empty($_COOKIE[$config['identity_cookie']])) {
|
||||
$payload = faceai_verify_payload($_COOKIE[$config['identity_cookie']], $config['shared_secret']);
|
||||
if (($payload['type'] ?? '') !== 'legacy-identity') {
|
||||
throw new RuntimeException('Unexpected identity cookie payload.');
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => (string) ($payload['userId'] ?? ''),
|
||||
'displayName' => (string) ($payload['displayName'] ?? ''),
|
||||
'email' => (string) ($payload['email'] ?? ''),
|
||||
'membershipStatus' => (string) ($payload['membershipStatus'] ?? 'inactive')
|
||||
);
|
||||
}
|
||||
|
||||
if ($config['allow_dev_handoff']) {
|
||||
$userId = faceai_request_value('devUserId');
|
||||
if ($userId !== '') {
|
||||
return array(
|
||||
'id' => $userId,
|
||||
'displayName' => faceai_request_value('devDisplayName', 'Local Test User'),
|
||||
'email' => faceai_request_value('devEmail', 'local.test@example.invalid'),
|
||||
'membershipStatus' => faceai_request_value('devMembershipStatus', 'active')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function faceai_render_message_page($title, $message, array $details = array(), $statusCode = 400)
|
||||
{
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
|
||||
echo '<!doctype html><html lang="it"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">';
|
||||
echo '<title>' . faceai_html($title) . '</title>';
|
||||
echo '<style>body{font-family:Georgia,serif;background:#f7f1e8;color:#2a231b;margin:0;padding:32px}main{max-width:900px;margin:0 auto;background:#fff;border:1px solid #ddcbb5;padding:24px}h1{margin-top:0}code{background:#f2ece2;padding:2px 5px}ul{padding-left:20px}li{margin:8px 0}</style>';
|
||||
echo '</head><body><main>';
|
||||
echo '<h1>' . faceai_html($title) . '</h1>';
|
||||
echo '<p>' . faceai_html($message) . '</p>';
|
||||
|
||||
if (!empty($details)) {
|
||||
echo '<ul>';
|
||||
foreach ($details as $detail) {
|
||||
echo '<li>' . faceai_html($detail) . '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
echo '</main></body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
function faceai_fetch_json($url)
|
||||
{
|
||||
$context = stream_context_create(array(
|
||||
'http' => array(
|
||||
'ignore_errors' => true,
|
||||
'timeout' => 10
|
||||
)
|
||||
));
|
||||
|
||||
$response = @file_get_contents($url, false, $context);
|
||||
if ($response === false) {
|
||||
throw new RuntimeException('Unable to fetch remote FaceAI data.');
|
||||
}
|
||||
|
||||
$statusCode = 0;
|
||||
if (!empty($http_response_header[0]) && preg_match('/\s(\d{3})\s/', $http_response_header[0], $matches)) {
|
||||
$statusCode = (int) $matches[1];
|
||||
}
|
||||
|
||||
$payload = json_decode($response, true);
|
||||
if (!is_array($payload)) {
|
||||
throw new RuntimeException('FaceAI returned invalid JSON.');
|
||||
}
|
||||
|
||||
if ($statusCode >= 400) {
|
||||
throw new RuntimeException($payload['error'] ?? ('FaceAI bridge request failed with status ' . $statusCode . '.'));
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
76
www/faceai_handoff.php
Normal file
76
www/faceai_handoff.php
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/faceai_config.php';
|
||||
|
||||
$config = faceai_config();
|
||||
|
||||
try {
|
||||
$raceId = faceai_request_value('raceId');
|
||||
$raceSlug = faceai_request_value('raceSlug');
|
||||
$raceName = faceai_request_value('raceName', $raceSlug !== '' ? $raceSlug : $raceId);
|
||||
$lang = faceai_request_value('lang', 'it');
|
||||
$returnUrl = faceai_request_value('returnUrl');
|
||||
|
||||
if ($raceId === '' || $returnUrl === '') {
|
||||
faceai_render_message_page(
|
||||
'FaceAI handoff non disponibile',
|
||||
'Mancano i parametri minimi richiesti per lanciare FaceAI.',
|
||||
array(
|
||||
'Parametri richiesti: raceId, returnUrl.',
|
||||
'Il pulsante Face ID deve passare anche raceSlug e lang quando disponibili.'
|
||||
),
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
$identity = faceai_resolve_identity($config);
|
||||
if ($identity === null) {
|
||||
faceai_render_message_page(
|
||||
'FaceAI handoff in attesa del bridge legacy',
|
||||
'Questo endpoint PHP non puo leggere la sessione Java esistente. Per funzionare in produzione deve ricevere una identita firmata dal layer legacy o dal reverse proxy.',
|
||||
array(
|
||||
'Opzione consigliata: cookie firmato ' . $config['identity_cookie'] . ' con payload type=legacy-identity.',
|
||||
'Per test locale e possibile passare devUserId, devDisplayName, devEmail e devMembershipStatus se FACEAI_ALLOW_DEV_HANDOFF=1.',
|
||||
'Esempio locale: faceai_handoff.php?raceId=101&raceSlug=mezza-di-firenze&lang=it&returnUrl=http%3A%2F%2Flocalhost%2Fold&devUserId=1&devDisplayName=Mario%20Rossi&devEmail=mario%40example.test&devMembershipStatus=active'
|
||||
),
|
||||
501
|
||||
);
|
||||
}
|
||||
|
||||
if (($identity['membershipStatus'] ?? 'inactive') !== 'active') {
|
||||
faceai_render_message_page(
|
||||
'FaceAI non disponibile',
|
||||
'L utente corrente non risulta abilitato all uso di FaceAI in base allo stato di membership.',
|
||||
array('Stato attuale: ' . ($identity['membershipStatus'] ?? 'unknown')),
|
||||
403
|
||||
);
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'type' => 'handoff',
|
||||
'user' => array(
|
||||
'id' => $identity['id'],
|
||||
'displayName' => $identity['displayName'],
|
||||
'email' => $identity['email'],
|
||||
'membershipStatus' => $identity['membershipStatus']
|
||||
),
|
||||
'race' => array(
|
||||
'id' => $raceId,
|
||||
'slug' => $raceSlug !== '' ? $raceSlug : $raceId,
|
||||
'name' => $raceName !== '' ? $raceName : $raceId
|
||||
),
|
||||
'lang' => $lang,
|
||||
'returnUrl' => $returnUrl,
|
||||
'expiresAt' => ((int) round(microtime(true) * 1000)) + (5 * 60 * 1000)
|
||||
);
|
||||
|
||||
$token = faceai_sign_payload($payload, $config['shared_secret']);
|
||||
$targetUrl = faceai_build_url($config['frontend_url'] . '/auth/callback', array('token' => $token));
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ' . $targetUrl, true, 302);
|
||||
exit;
|
||||
} catch (Throwable $error) {
|
||||
faceai_render_message_page('Errore handoff FaceAI', $error->getMessage(), array(), 500);
|
||||
}
|
||||
56
www/faceai_return.php
Normal file
56
www/faceai_return.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/faceai_config.php';
|
||||
require_once __DIR__ . '/faceai_simulator_view.php';
|
||||
|
||||
$config = faceai_config();
|
||||
|
||||
try {
|
||||
$resultId = faceai_request_value('resultId');
|
||||
$token = faceai_request_value('token');
|
||||
|
||||
if ($resultId === '' || $token === '') {
|
||||
faceai_render_message_page(
|
||||
'FaceAI return non disponibile',
|
||||
'Mancano resultId o token nella chiamata di ritorno da FaceAI.',
|
||||
array(),
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
$payload = faceai_verify_payload($token, $config['shared_secret']);
|
||||
if (($payload['type'] ?? '') !== 'return') {
|
||||
throw new RuntimeException('Wrong return token type.');
|
||||
}
|
||||
|
||||
if ((string) ($payload['resultId'] ?? '') !== $resultId) {
|
||||
throw new RuntimeException('Result id mismatch.');
|
||||
}
|
||||
|
||||
if ($config['return_forward_url'] !== '') {
|
||||
header('Location: ' . faceai_build_url($config['return_forward_url'], array(
|
||||
'resultId' => $resultId,
|
||||
'token' => $token
|
||||
)), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
$bridgeUrl = faceai_build_url($config['backend_internal_url'] . '/bridge/results/' . rawurlencode($resultId), array(
|
||||
'token' => $token
|
||||
));
|
||||
$result = faceai_fetch_json($bridgeUrl);
|
||||
|
||||
faceai_sim_render_page(array(
|
||||
'raceId' => (string) ($result['raceId'] ?? ($payload['raceId'] ?? '')),
|
||||
'lang' => (string) ($result['lang'] ?? 'it'),
|
||||
'raceSlug' => (string) ($result['raceId'] ?? ($payload['raceId'] ?? '')),
|
||||
'raceName' => (string) ($result['raceName'] ?? ('Race ' . ($payload['raceId'] ?? ''))),
|
||||
'returnUrl' => (string) ($result['returnUrl'] ?? 'faceai_simulator.php'),
|
||||
'banner' => 'Vista filtrata da FaceAI. Sono state trovate <strong>' . count($result['matches'] ?? array()) . '</strong> foto corrispondenti per l utente corrente.',
|
||||
'totalLabel' => count($result['matches'] ?? array()) . ' foto da FaceAI',
|
||||
'photos' => is_array($result['matches'] ?? null) ? $result['matches'] : array(),
|
||||
'showSimulatorBootstrap' => false
|
||||
));
|
||||
} catch (Throwable $error) {
|
||||
faceai_render_message_page('Errore return FaceAI', $error->getMessage(), array(), 500);
|
||||
}
|
||||
35
www/faceai_simulator.php
Normal file
35
www/faceai_simulator.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/faceai_simulator_view.php';
|
||||
|
||||
$raceId = isset($_GET['raceId']) ? trim((string) $_GET['raceId']) : '101';
|
||||
$lang = isset($_GET['lang']) ? trim((string) $_GET['lang']) : 'it';
|
||||
$raceSlug = isset($_GET['raceSlug']) ? trim((string) $_GET['raceSlug']) : 'mezza-di-firenze';
|
||||
$raceName = isset($_GET['raceName']) ? trim((string) $_GET['raceName']) : 'Mezza di Firenze';
|
||||
$returnUrl = 'http://localhost:8080/faceai_simulator.php?raceId=' . rawurlencode($raceId) . '&lang=' . rawurlencode($lang) . '&raceSlug=' . rawurlencode($raceSlug) . '&raceName=' . rawurlencode($raceName);
|
||||
|
||||
$photos = array(
|
||||
array('id' => 'f101-001', 'thumb' => 'thumb-arrivo-001.jpg', 'label' => 'Arrivo 001', 'checkpoint' => 'Arrivo'),
|
||||
array('id' => 'f101-002', 'thumb' => 'thumb-arrivo-002.jpg', 'label' => 'Arrivo 002', 'checkpoint' => 'Arrivo'),
|
||||
array('id' => 'f101-003', 'thumb' => 'thumb-ponte-003.jpg', 'label' => 'Ponte 003', 'checkpoint' => 'Ponte'),
|
||||
array('id' => 'f101-004', 'thumb' => 'thumb-centro-004.jpg', 'label' => 'Centro 004', 'checkpoint' => 'Centro'),
|
||||
array('id' => 'f101-005', 'thumb' => 'thumb-centro-005.jpg', 'label' => 'Centro 005', 'checkpoint' => 'Centro'),
|
||||
array('id' => 'f101-006', 'thumb' => 'thumb-arrivo-006.jpg', 'label' => 'Arrivo 006', 'checkpoint' => 'Arrivo'),
|
||||
array('id' => 'f101-007', 'thumb' => 'thumb-ponte-007.jpg', 'label' => 'Ponte 007', 'checkpoint' => 'Ponte'),
|
||||
array('id' => 'f101-008', 'thumb' => 'thumb-centro-008.jpg', 'label' => 'Centro 008', 'checkpoint' => 'Centro'),
|
||||
array('id' => 'f101-009', 'thumb' => 'thumb-arrivo-009.jpg', 'label' => 'Arrivo 009', 'checkpoint' => 'Arrivo'),
|
||||
array('id' => 'f101-010', 'thumb' => 'thumb-lungarno-010.jpg', 'label' => 'Lungarno 010', 'checkpoint' => 'Lungarno'),
|
||||
array('id' => 'f101-011', 'thumb' => 'thumb-piazza-011.jpg', 'label' => 'Piazza 011', 'checkpoint' => 'Piazza'),
|
||||
array('id' => 'f101-012', 'thumb' => 'thumb-arrivo-012.jpg', 'label' => 'Arrivo 012', 'checkpoint' => 'Arrivo')
|
||||
);
|
||||
|
||||
faceai_sim_render_page(array(
|
||||
'raceId' => $raceId,
|
||||
'lang' => $lang,
|
||||
'raceSlug' => $raceSlug,
|
||||
'raceName' => $raceName,
|
||||
'returnUrl' => $returnUrl,
|
||||
'banner' => 'Questa pagina PHP simula il punto di ingresso del sito legacy. Il vecchio select con ID <strong>tipoPuntoFoto</strong> viene rimosso dal JavaScript originale e sostituito dal pulsante Face ID.',
|
||||
'totalLabel' => count($photos) . ' foto demo',
|
||||
'photos' => $photos,
|
||||
'showSimulatorBootstrap' => true
|
||||
));
|
||||
184
www/faceai_simulator_view.php
Normal file
184
www/faceai_simulator_view.php
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
function faceai_sim_html($value)
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function faceai_sim_render_page(array $options)
|
||||
{
|
||||
$raceId = $options['raceId'];
|
||||
$lang = $options['lang'];
|
||||
$raceSlug = $options['raceSlug'];
|
||||
$raceName = $options['raceName'];
|
||||
$returnUrl = $options['returnUrl'];
|
||||
$banner = $options['banner'];
|
||||
$totalLabel = $options['totalLabel'];
|
||||
$photos = $options['photos'];
|
||||
$showSimulatorBootstrap = !empty($options['showSimulatorBootstrap']);
|
||||
|
||||
?><!doctype html>
|
||||
<html lang="<?php echo faceai_sim_html($lang); ?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>FaceAI Legacy Simulator</title>
|
||||
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/font-awesome.min.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i" rel="stylesheet">
|
||||
<link href="css/custom-style.css" rel="stylesheet">
|
||||
<style>
|
||||
.page-shell {
|
||||
max-width: 1120px;
|
||||
margin: 32px auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.sim-banner {
|
||||
background: #efe4d2;
|
||||
border: 1px solid #c9ab83;
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.gallery-grid {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
}
|
||||
.gallery-card {
|
||||
background: #fff;
|
||||
border: 1px solid #decbb5;
|
||||
padding: 16px;
|
||||
}
|
||||
.gallery-thumb {
|
||||
min-height: 120px;
|
||||
background: #efe4d2;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 12px;
|
||||
color: #6d5a46;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gallery-thumb img {
|
||||
max-width: 100%;
|
||||
max-height: 120px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a id="top"></a>
|
||||
<nav class="navbar fixed-top navbar-expand-lg navbar-light bg-white fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="faceai_simulator.php?raceId=<?php echo faceai_sim_html($raceId); ?>&lang=<?php echo faceai_sim_html($lang); ?>"><img src="images/layout/regalami-un-sorriso-ets-640.png" alt="Regalami Un Sorriso Ets" width="100"></a>
|
||||
<button class="navbar-toggler navbar-toggler-right" type="button"><span class="navbar-toggler-icon"></span></button>
|
||||
<div class="collapse navbar-collapse show" id="navbarResponsive">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item"><a class="nav-link" href="index.jsp">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="associazione.jsp">Associazione</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="faceai_simulator.php?raceId=<?php echo faceai_sim_html($raceId); ?>&lang=<?php echo faceai_sim_html($lang); ?>">Foto</a></li>
|
||||
<li class="nav-item dropdown show"><a class="nav-link btn btn-sm btn-warning dropdown-toggle" href="#">Archivio</a></li>
|
||||
<li class="nav-item"><a href="#"><img src="images/btn_donateCC_LG.gif" border="0" alt="PayPal"></a></li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item dropdown"><a class="nav-link dropdown-toggle active" href="#"><i class="fa fa-user" aria-hidden="true"></i> Il mio account</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="https://it-it.facebook.com/pg/Regalami-un-sorriso-ETS-189377806523/community/"><img src="images/FB-f-Logo__blue_29.png" class="img-fluid" alt="Facebook"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container my-3 page-shell">
|
||||
<div class="row mb-5">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="my-4"><?php echo faceai_sim_html($raceName); ?></h1>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<img src="images/layout/Logo_RUS_ETS_tricolore_3-1.jpg" class="img-fluid border border-warning" alt="Gara">
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<div class="row riepilogo">
|
||||
<div class="col-md-3"><p><i class="fa fa-map-marker fa-lg text-warning" aria-hidden="true"></i> Firenze</p></div>
|
||||
<div class="col-md-3"><p><i class="fa fa-calendar fa-lg text-warning" aria-hidden="true"></i> 07/04/2026</p></div>
|
||||
<div class="col"><p><i class="fa fa-camera-retro fa-lg text-warning"></i> <?php echo faceai_sim_html($totalLabel); ?></p></div>
|
||||
</div>
|
||||
<div class="sim-banner"><?php echo $banner; ?></div>
|
||||
|
||||
<form class="bg-light p-3 border" onsubmit="return searching()">
|
||||
<input name="id_gara" id="id_gara" type="hidden" value="<?php echo faceai_sim_html($raceId); ?>">
|
||||
<input name="id_foto" id="id_foto" type="hidden">
|
||||
<input name="garaDesc" id="garaDesc" type="hidden" value="<?php echo faceai_sim_html($raceSlug); ?>">
|
||||
<input name="lang" id="lang" type="hidden" value="<?php echo faceai_sim_html($lang); ?>">
|
||||
<input name="pageNumber" id="pageNumber" type="hidden" value="1">
|
||||
<input name="actionPage" id="actionPage" type="hidden" value="Foto.abl">
|
||||
<input name="totPageNumber" id="totPageNumber" type="hidden" value="5">
|
||||
<div class="row align-items-end">
|
||||
<div class="form-group col-12 col-md-4">
|
||||
<label for="id_puntoFoto">Punti Foto</label>
|
||||
<select name="id_puntoFoto" id="id_puntoFoto" onchange="searchingPF()" class="custom-select form-control form-control-sm mb-2 mb-sm-0">
|
||||
<option value="">-- Punti Foto --</option>
|
||||
<option value="arrivo">Arrivo</option>
|
||||
<option value="centro">Centro</option>
|
||||
<option value="ponte">Ponte</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-12 col-md-4">
|
||||
<label for="tipoPuntoFoto">Descrizione/Orario</label>
|
||||
<select name="tipoPuntoFoto" id="tipoPuntoFoto" onchange="searchingTPF()" class="custom-select form-control form-control-sm mb-2 mb-sm-0">
|
||||
<option value="">-- Descrizione/Orario --</option>
|
||||
<option value="arrivo-09-15">Arrivo 09:15</option>
|
||||
<option value="centro-08-45">Centro 08:45</option>
|
||||
<option value="ponte-08-10">Ponte 08:10</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-12 col-md-2">
|
||||
<label for="pageRow">Foto per pagina</label>
|
||||
<select name="pageRow" id="pageRow" class="custom-select form-control form-control-sm mb-2 mb-sm-0">
|
||||
<option value="24">24</option>
|
||||
<option value="36">36</option>
|
||||
<option value="48">48</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-12 col-md-2">
|
||||
<label for="pettorale">Pettorale</label>
|
||||
<input name="pettorale" id="pettorale" value="245" class="form-control form-control-sm mb-2 mb-sm-0">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gallery-grid">
|
||||
<?php foreach ($photos as $photo): ?>
|
||||
<div class="gallery-card">
|
||||
<div class="gallery-thumb">
|
||||
<?php if (!empty($photo['previewUrl'])): ?>
|
||||
<img src="<?php echo faceai_sim_html($photo['previewUrl']); ?>" alt="<?php echo faceai_sim_html($photo['label']); ?>">
|
||||
<?php else: ?>
|
||||
<?php echo faceai_sim_html($photo['thumb'] ?? $photo['id']); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<strong><?php echo faceai_sim_html($photo['label'] ?? $photo['id']); ?></strong><br>
|
||||
<small>ID foto: <?php echo faceai_sim_html($photo['id'] ?? ''); ?></small><br>
|
||||
<small>Punto foto: <?php echo faceai_sim_html($photo['checkpoint'] ?? '-'); ?></small>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($showSimulatorBootstrap): ?>
|
||||
<script>
|
||||
window.faceAiSimulator = {
|
||||
handoffUrl: 'faceai_handoff.php',
|
||||
returnUrl: <?php echo json_encode($returnUrl); ?>,
|
||||
devUserId: '1',
|
||||
devDisplayName: 'Mario Rossi',
|
||||
devEmail: 'mario.rossi@example.test',
|
||||
devMembershipStatus: 'active'
|
||||
};
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
<script src="vendor/jquery/jquery.min.js"></script>
|
||||
<script src="vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="_js/rus-ecom-240621.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue