added new capabilities in iframe

This commit is contained in:
Luca Haid
2025-07-15 09:17:26 +02:00
parent 4148e1749f
commit 72ff13e621
3 changed files with 231 additions and 597 deletions
@@ -1,31 +1,22 @@
<?php
// in /controllers/PreorderIFrameController.php
class PreorderIFrameController extends mfBaseController
{
private PreorderIFrameModel $preorderIFrameModel;
public function init()
{
// The model is autoloaded or included elsewhere
// 'X-Requested-With': 'XMLHttpRequest', 'X-Frame-Options': 'SAMEORIGIN', 'X-Frame-Referrer': document.referrer
$this->preorderIFrameModel = new PreorderIFrameModel();
}
/**
* Serves the main order form HTML.
* This action injects the necessary configuration into the Vue app.
*/
public function indexAction()
{
$clusterId = $this->request->get('clusterId', 'NULL');
$color = $this->request->get('color', 'blue');
$vue_config = [
'baseUrl' => '/PreorderIFrame', // URL to this controller
'clusterId' => $clusterId !== NULL ? intval($clusterId) : null,
'color' => htmlspecialchars($color),
'baseUrl' => '/PreorderIFrame',
'clusterId' => $this->request->get('clusterId') ? intval($this->request->get('clusterId')) : null,
'gemeindeId' => $this->request->get('gemeindeId') ? intval($this->request->get('gemeindeId')) : null,
'color' => htmlspecialchars($this->request->get('color', 'blue')),
'orderType' => htmlspecialchars($this->request->get('orderType', 'order')), // 'order' or 'interest'
];
$this->layout()->set("JSGlobals", $vue_config);
@@ -34,7 +25,8 @@ class PreorderIFrameController extends mfBaseController
// --- API ENDPOINTS ---
public function getClustersAction() {
public function getClustersAction()
{
self::returnJson(['clusters' => $this->preorderIFrameModel->getClusters($_SERVER['HTTP_X_FRAME_REFERRER'])]);
}
@@ -44,8 +36,6 @@ class PreorderIFrameController extends mfBaseController
if (!$clusterId) self::sendError("Cluster ID is required.");
$allClusters = $this->preorderIFrameModel->getClusters($_SERVER['HTTP_X_FRAME_REFERRER']);
if (!$allClusters) self::sendError("No cluster found for the given ID.");
$clusterInfo = null;
foreach ($allClusters as $cluster) {
if ($cluster['id'] == $clusterId) {
@@ -54,49 +44,59 @@ class PreorderIFrameController extends mfBaseController
}
}
$preorderCampaign = new Preordercampaign($clusterInfo['campaign_id']);
if (!$clusterInfo) self::sendError("No cluster found for the given ID or origin.");
$preorderCampaign = new Preordercampaign($clusterInfo['campaign_id']);
self::returnJson(['iframe_consents' => json_decode($preorderCampaign->iframe_consents ?? '[]')]);
}
public function findCityAction()
{
$allowedClusters = $this->preorderIFrameModel->getClusters($_SERVER['HTTP_X_FRAME_REFERRER']);
$zip = $this->request->get('zip');
$clusterId = $this->request->get('cluster_id');
$cities = $this->preorderIFrameModel->findCities($zip, $clusterId);
self::returnJson(['cities' => $cities]);
$params = [
'zip' => $this->request->get('zip'),
'clusterId' => $this->request->get('cluster_id'),
'gemeindeId' => $this->request->get('gemeinde_id'),
];
self::returnJson(['cities' => $this->preorderIFrameModel->findCities($params)]);
}
public function findStreetAction()
{
// $this->checkOriginAndGetCampaign(); // Security check
$zip = $this->request->get('zip');
$city = $this->request->get('city');
$clusterId = $this->request->get('cluster_id');
$streets = $this->preorderIFrameModel->findStreets($zip, $city, $clusterId);
self::returnJson(['streets' => $streets]);
$params = [
'zip' => $this->request->get('zip'),
'city' => $this->request->get('city'),
'clusterId' => $this->request->get('cluster_id'),
'gemeindeId' => $this->request->get('gemeinde_id'),
];
self::returnJson(['streets' => $this->preorderIFrameModel->findStreets($params)]);
}
public function findAddressAction()
{
$addresses = $this->preorderIFrameModel->findAddresses($_GET);
self::returnJson(['addresses' => $addresses]);
self::returnJson(['addresses' => $this->preorderIFrameModel->findAddresses($_GET)]);
}
public function submitOrderAction()
{
$requestBody = file_get_contents('php://input');
$preorderData = json_decode($requestBody, true);
$preorderData = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) self::sendError("Invalid JSON data.");
$tt_network = NetworkModel::getFirst(['adb_network_id' => $preorderData['additionalData']['clusterId']]);
if (!$tt_network) self::sendError("No network found for the given cluster ID.");
// Determine network/campaign from clusterId or gemeindeId
$networkId = null;
if (!empty($preorderData['additionalData']['clusterId'])) {
$tt_network = NetworkModel::getFirst(['adb_network_id' => $preorderData['additionalData']['clusterId']]);
if ($tt_network) $networkId = $tt_network->id;
} elseif (!empty($preorderData['additionalData']['gemeindeId'])) {
$gn = GemeindeNetzgebietModel::getFirst(['gemeinde_id' => $preorderData['additionalData']['gemeindeId']]);
if ($gn) {
$tt_network = NetworkModel::getFirst(['adb_netzgebiet_id' => $gn->netzgebiet_id]);
if ($tt_network) $networkId = $tt_network->id;
}
}
$campaign = PreordercampaignModel::getFirst(['network_id' => $tt_network->id]);
if (!$campaign) self::sendError("No campaign found for the given cluster ID.");
if (!$networkId) self::sendError("No network/campaign found for the given area.");
$campaign = PreordercampaignModel::getFirst(['network_id' => $networkId]);
if (!$campaign) self::sendError("No campaign found for the given area.");
$h = new ADBHausnummer($preorderData['address']['hausnummer_id']);
if (!$h->id) self::sendError("Invalid house number ID provided.");
@@ -109,7 +109,6 @@ class PreorderIFrameController extends mfBaseController
$data['adb_hausnummer_id'] = $preorderData['address']['hausnummer_id'];
$data['adb_wohneinheit_id'] = $preorderData['address']['wohneinheit_id'];
$new_status = null;
if ($data['adb_wohneinheit_id'] && $w->id) {
$status_code = max($w->status->code, $w->hausnummer->status->code);
@@ -119,7 +118,7 @@ class PreorderIFrameController extends mfBaseController
}
$data["status_id"] = $new_status ? $new_status->id : 1;
$data['type'] = $preorderData['connectionType'] === 'vorsorge' ? 'provision' : 'order';
$data['type'] = $preorderData['preorderType'] ?? 'order'; // 'order', 'interest', 'provision'
$data['connection_type'] = $preorderData['customerType'] === 'business' ? 'business' : 'single-dwelling';
$data['accept_agb'] = $preorderData['acceptAgb'] ? 1 : 0;
@@ -137,17 +136,12 @@ class PreorderIFrameController extends mfBaseController
$data['city'] = (trim($preorderData['customer']['city'])) ?: null;
$data['phone'] = (trim($preorderData['customer']['phone'])) ?: null;
$data['email'] = (trim($preorderData['customer']['email'])) ?: null;
$data['edit_by'] = 1;
$data['create_by'] = 1;
$preorder = PreorderModel::create($data);
$preorder->createUcode();
$new_id = $preorder->save();
if (!$new_id) {
self::sendError("Failed to create preorder record.");
}
if (!$preorder->save()) self::sendError("Failed to create preorder record.");
self::returnJson(['orderCode' => $preorder->ucode, 'status' => 'success']);
}
@@ -2,46 +2,9 @@
class PreorderIFrameModel extends mfBaseModel
{
// The constructor will use the default FronkDB connection
// No need to override __construct if mfBaseModel handles it
public function init() {
// Overwrite the table name if it doesn't match the class name
$this->table = 'Preorder'; // Set a primary table if needed, though most methods define their own
}
/**
* Checks if an origin has rights for a given cluster and returns the campaign.
* @param int $clusterId The 'adb_netzgebiet_id' from the thetool.Network table.
* @param string|null $origin The requesting origin (e.g., https://www.example.com).
* @return array|null The campaign data if valid, otherwise null.
*/
public function getCampaignByClusterIdAndOrigin(int $clusterId, ?string $origin): ?array
public function init()
{
// A null origin is not allowed for security reasons
if (!$origin) {
return null;
}
$query = "
SELECT pc.*
FROM thetool.Preordercampaign pc
JOIN thetool.Network n ON pc.Network_id = n.id
WHERE n.adb_netzgebiet_id = ?
";
$res = $this->db->query($query, [$clusterId]);
$campaign = $this->db->fetch_assoc($res);
if (!$campaign || empty($campaign['iframe_origins'])) {
return null;
}
$allowedOrigins = json_decode($campaign['iframe_origins'], true);
if (is_array($allowedOrigins) && in_array($origin, $allowedOrigins)) {
return $campaign;
}
return null;
$this->table = 'Preorder';
}
public function getClusters($frame_referrer): array
@@ -59,78 +22,98 @@ class PreorderIFrameModel extends mfBaseModel
$res = $this->db->query($query);
$clusters = $this->db->fetch_all_assoc($res);
if (empty($clusters) || count($clusters) === 0) {
return [
['id' => 98, 'name' => 'Leibnitz', 'campaign_id' => 99, 'campaign_name' => 'X - Leibnitz']
];
if (empty($clusters)) {
// Fallback for development or specific cases
return [['id' => 98, 'name' => 'Leibnitz', 'campaign_id' => 99, 'campaign_name' => 'X - Leibnitz']];
}
return $clusters;
}
public function findCities(string $zip, int $adb_network_id): array
public function findCities(array $params): array
{
$whereClause = "p.plzstring = " . $this->db->escape($params['zip']);
if (!empty($params['gemeindeId'])) {
$whereClause .= " AND g.id = " . intval($params['gemeindeId']);
} elseif (!empty($params['clusterId'])) {
$whereClause .= " AND gn.netzgebiet_id = " . intval($params['clusterId']);
} else {
return []; // No identifier provided
}
$query = "
SELECT DISTINCT o.name
FROM addressdb.Plz p
JOIN addressdb.Ortschaft o ON p.gemeinde_id = o.gemeinde_id
JOIN addressdb.Gemeinde g ON o.gemeinde_id = g.id
JOIN addressdb.GemeindeNetzgebiet gn ON g.id = gn.gemeinde_id
WHERE p.plzstring = " . $this->db->escape($zip) . " AND gn.netzgebiet_id = " . intval($adb_network_id) . "
LEFT JOIN addressdb.GemeindeNetzgebiet gn ON g.id = gn.gemeinde_id
WHERE $whereClause
ORDER BY o.name ASC
";
$res = $this->db->query($query);
$cities = $this->db->fetch_all_assoc($res);
return array_column($cities, 'name');
return array_column($this->db->fetch_all_assoc($res), 'name');
}
/**
* Finds streets for a given ZIP, city, and cluster.
* @param string $zip
* @param string $city
* @param int $clusterId
* @return array
*/
public function findStreets(string $zip, string $city, int $clusterId): array
public function findStreets(array $params): array
{
$whereClauses = [];
if (!empty($params['gemeindeId'])) {
$whereClauses[] = "g.id = " . intval($params['gemeindeId']);
} elseif (!empty($params['clusterId'])) {
$whereClauses[] = "gn.netzgebiet_id = " . intval($params['clusterId']);
} else {
return [];
}
$whereClauses[] = "o.name = '" . $this->db->escape($params['city']) . "'";
$whereClauses[] = "EXISTS (SELECT 1 FROM addressdb.Plz p WHERE p.plzstring = " . $this->db->escape($params['zip']) . " AND p.gemeinde_id = o.gemeinde_id)";
$whereString = implode(" AND ", $whereClauses);
$query = "
SELECT DISTINCT s.name
FROM addressdb.Strasse s
JOIN addressdb.Gemeinde g ON s.gemeinde_id = g.id
JOIN addressdb.Ortschaft o ON o.gemeinde_id = g.id AND o.name = '" . $this->db->escape($city) . "'
JOIN addressdb.GemeindeNetzgebiet gn ON g.id = gn.gemeinde_id
WHERE gn.netzgebiet_id = " . intval($clusterId) . "
AND EXISTS (
SELECT 1
FROM addressdb.Plz p
WHERE p.plzstring = " . $this->db->escape($zip) . "
AND p.gemeinde_id = o.gemeinde_id
)
JOIN addressdb.Ortschaft o ON o.gemeinde_id = g.id
LEFT JOIN addressdb.GemeindeNetzgebiet gn ON g.id = gn.gemeinde_id
WHERE $whereString
ORDER BY s.name ASC
";
$res = $this->db->query($query);
$streets = $this->db->fetch_all_assoc($res);
return array_column($streets, 'name');
return array_column($this->db->fetch_all_assoc($res), 'name');
}
public function findAddresses(array $params): array
{
$whereClauses = [
"p.plzstring = " . $this->db->escape($params['zip']),
"o.name = '" . $this->db->escape($params['city']) . "'",
"s.name = '" . $this->db->escape($params['street']) . "'",
"h.hausnummer = '" . $this->db->escape($params['housenumber']) . "'",
];
if (!empty($params['gemeinde_id'])) {
$whereClauses[] = "h.gemeinde_id = " . intval($params['gemeinde_id']);
} elseif (!empty($params['cluster_id'])) {
$whereClauses[] = "h.netzgebiet_id = " . intval($params['cluster_id']);
} else {
return [];
}
$whereString = implode(" AND ", $whereClauses);
$query = "
SELECT h.oaid, h.hausnummer, s.name as street, p.plzstring as zip, o.name as city, h.id as hausnummer_id, w.id as wohneinheit_id,
h.stiege, h.unit_count as building_unit_count, h.tool_building_type as building_type,
w.oaid as unit_oaid, w.stock, w.tuer, w.zusatz
FROM addressdb.Hausnummer h
JOIN addressdb.Strasse s ON h.strasse_id = s.id
JOIN addressdb.Plz p ON h.plz_id = p.id
JOIN addressdb.Ortschaft o ON s.gemeinde_id = o.gemeinde_id
LEFT JOIN addressdb.Wohneinheit w ON w.hausnummer_id = h.id
WHERE h.netzgebiet_id = " . intval($params['cluster_id']) . "
AND p.plzstring = " . $this->db->escape($params['zip']) . "
AND o.name = '" . $this->db->escape($params['city']) . "'
AND s.name = '" . $this->db->escape($params['street']) . "'
AND h.hausnummer = '" . $this->db->escape($params['housenumber']) . "'
";
SELECT h.oaid, h.hausnummer, s.name as street, p.plzstring as zip, o.name as city, h.id as hausnummer_id, w.id as wohneinheit_id,
h.stiege, h.unit_count as building_unit_count, h.tool_building_type as building_type,
w.oaid as unit_oaid, w.stock, w.tuer, w.zusatz
FROM addressdb.Hausnummer h
JOIN addressdb.Strasse s ON h.strasse_id = s.id
JOIN addressdb.Plz p ON h.plz_id = p.id
JOIN addressdb.Ortschaft o ON s.gemeinde_id = o.gemeinde_id
LEFT JOIN addressdb.Wohneinheit w ON w.hausnummer_id = h.id
WHERE $whereString
";
$results = $this->db->fetch_all_assoc($this->db->query($query));
if (empty($results)) return [];
@@ -138,51 +121,40 @@ class PreorderIFrameModel extends mfBaseModel
$addresses = [];
$topCounter = 1;
if (count($results) > 1) {
if (count($results) > 1 && $results[0]['wohneinheit_id'] !== null) {
foreach ($results as $row) {
$showText = $this->buildShowText($row, $topCounter++);
$addresses[] = [
'oaid' => $row['unit_oaid'],
'street' => $row['street'],
'housenumber' => $row['hausnummer'],
'hausnummer_id' => $row['hausnummer_id'],
'wohneinheit_id' => $row['wohneinheit_id'],
'building_type' => intval($row['building_type']),
'zip' => $row['zip'],
'city' => $row['city'],
'stiege' => $row['stiege'],
'stock' => $row['stock'],
'tuer' => $row['tuer'],
'zusatz' => $row['zusatz'],
'building_unit_count' => $row['building_unit_count'],
'showText' => $showText,
'preorderTypes' => ['order']
];
$addresses[] = array_merge($this->formatAddressRow($row), [
'showText' => $this->buildShowText($row, $topCounter++)
]);
}
} else {
$row = $results[0];
$addresses[] = [
'oaid' => $row['oaid'],
'street' => $row['street'],
'housenumber' => $row['hausnummer'],
'hausnummer_id' => $row['hausnummer_id'],
'wohneinheit_id' => $row['wohneinheit_id'],
'building_type' => intval($row['building_type']),
'zip' => $row['zip'],
'city' => $row['city'],
'stiege' => $row['stiege'],
'stock' => $row['stock'],
'tuer' => $row['tuer'],
'zusatz' => $row['zusatz'],
'building_unit_count' => $row['building_unit_count'],
'showText' => $this->buildShowText($row, 1),
'preorderTypes' => ['order']
];
$addresses[] = $this->formatAddressRow($results[0]);
}
return $addresses;
}
private function formatAddressRow(array $row): array
{
return [
'oaid' => $row['unit_oaid'] ?? $row['oaid'],
'street' => $row['street'],
'housenumber' => $row['hausnummer'],
'hausnummer_id' => $row['hausnummer_id'],
'wohneinheit_id' => $row['wohneinheit_id'],
'building_type' => intval($row['building_type']),
'zip' => $row['zip'],
'city' => $row['city'],
'stiege' => $row['stiege'],
'stock' => $row['stock'],
'tuer' => $row['tuer'],
'zusatz' => $row['zusatz'],
'building_unit_count' => $row['building_unit_count'],
'showText' => $this->buildShowText($row, 1),
'preorderTypes' => ['order', 'interest'] // Can be used for both
];
}
private function buildShowText(array $row, int $counter): string
{
$parts = array_filter([
@@ -194,54 +166,4 @@ class PreorderIFrameModel extends mfBaseModel
return $parts ? implode(', ', $parts) : "Top {$counter}";
}
/**
* Creates a new preorder record in the database.
* @param array $data The validated preorder data from the form.
* @param int $campaignId The ID of the associated preorder campaign.
* @return array The result containing the new order code.
*/
public function createPreorder(array $data, int $campaignId): array
{
$customer = $data['customer'];
$address = $data['address'];
// Generate a unique code for the preorder
$ucode = strtoupper(substr(md5(uniqid(rand(), true)), 0, 10));
$preorderData = [
'ucode' => $ucode,
'thetool.Preordercampaign_id' => $campaignId,
'oaid' => $address['oaid'],
'type' => $data['preorderType'],
'connection_type' => $data['connectionType'],
'contact_type' => $customer['type'],
'firstname' => $customer['firstname'],
'lastname' => $customer['lastname'],
'company' => $customer['company'] ?? null,
'street' => $customer['street'],
'housenumber' => $customer['housenumber'],
'zip' => $customer['zip'],
'city' => $customer['city'],
'phone' => $customer['phone'],
'email' => $customer['email'],
'address_info' => $data['address_info'],
'accept_agb' => $data['acceptAgb'] ? 1 : 0,
'accept_dsgvo' => $data['acceptDsgvo'] ? 1 : 0,
'accept_marketing' => $data['acceptMarketing'] ? 1 : 0,
'accept_withdrawal' => $data['acceptWithdrawal'] ? 1 : 0,
'addon_data' => json_encode($data['additionalData']),
'submit_type' => 'api',
'submit_request' => json_encode($data),
'order_date' => time(),
'create' => time(),
'edit' => time(),
'create_by' => 0, // System user
'edit_by' => 0, // System user
];
$this->db->insert('Preorder', $preorderData);
return ['code' => $ucode];
}
}