| @@ -229,9 +229,22 @@ | ||
| 229 | 229 | return self::getUploadedFileUrl(Arr::get(self::$store['custom_booking_data'], $key)); |
| 230 | 230 | } |
| 231 | 231 | |
| 232 | 232 | if (Arr::get($customField, 'type') == 'hidden') { |
| 233 | - return self::parseShortCodes(Arr::get(self::$store['custom_booking_data'], $key)); | |
| 233 | + static $resolving = []; | |
| 234 | + | |
| 235 | + // Guest-supplied hidden values are parsed again, so a self-reference would recurse until the stack runs out. | |
| 236 | + if (isset($resolving[$key])) { | |
| 237 | + return Arr::get(self::$store['custom_booking_data'], $key); | |
| 238 | + } | |
| 239 | + | |
| 240 | + $resolving[$key] = true; | |
| 241 | + | |
| 242 | + try { | |
| 243 | + return self::parseShortCodes(Arr::get(self::$store['custom_booking_data'], $key)); | |
| 244 | + } finally { | |
| 245 | + unset($resolving[$key]); | |
| 246 | + } | |
| 234 | 247 | } |
| 235 | 248 | |
| 236 | 249 | return Arr::get(self::$store['custom_booking_data'], $key); |
| 237 | 250 | } |
| @@ -289,14 +302,48 @@ | ||
| 289 | 302 | return $guest->getTotalGuestCount(); |
| 290 | 303 | } |
| 291 | 304 | |
| 292 | 305 | if ($key == 'form_data_html') { |
| 293 | - return __('will be available soon', 'fluent-booking'); | |
| 306 | + // isPublic: hidden fields stay out, the recipient may be the guest. | |
| 307 | + return self::renderFormData( | |
| 308 | + BookingFieldService::getFormattedCustomBookingData($guest, static::$requireHtml, true), | |
| 309 | + static::$requireHtml | |
| 310 | + ); | |
| 294 | 311 | } |
| 295 | 312 | |
| 296 | - return Arr::get($guest, $key, ''); | |
| 313 | + return self::resolveScalarAttribute($guest, $key); | |
| 297 | 314 | } |
| 298 | 315 | |
| 316 | + protected static function renderFormData($fields, $isHtml) | |
| 317 | + { | |
| 318 | + $rows = ''; | |
| 319 | + | |
| 320 | + foreach ($fields as $field) { | |
| 321 | + $value = Arr::get($field, 'value'); | |
| 322 | + | |
| 323 | + if ($value === '' || $value === null || $value === []) { | |
| 324 | + continue; | |
| 325 | + } | |
| 326 | + | |
| 327 | + if (!$isHtml) { | |
| 328 | + $rows .= $field['label'] . ': ' . $value . PHP_EOL; | |
| 329 | + continue; | |
| 330 | + } | |
| 331 | + | |
| 332 | + // File values are download anchors; everything else is raw guest input. | |
| 333 | + $value = Arr::get($field, 'type') == 'file' ? wp_kses_post($value) : nl2br(esc_html($value)); | |
| 334 | + | |
| 335 | + $rows .= '<tr><th style="padding:6px 12px;background-color:#f8f8f8;text-align:' . (is_rtl() ? 'right' : 'left') . ';">' . esc_html($field['label']) . '</th></tr>' | |
| 336 | + . '<tr><td style="padding:6px 12px 12px 12px;">' . $value . '</td></tr>'; | |
| 337 | + } | |
| 338 | + | |
| 339 | + if (!$isHtml || !$rows) { | |
| 340 | + return trim($rows); | |
| 341 | + } | |
| 342 | + | |
| 343 | + return '<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;"><tbody>' . $rows . '</tbody></table>'; | |
| 344 | + } | |
| 345 | + | |
| 299 | 346 | protected static function getBookingEventData($key) |
| 300 | 347 | { |
| 301 | 348 | $bookingEvent = static::$store['booking_event']; |
| 302 | 349 | |
| @@ -305,13 +352,13 @@ | ||
| 305 | 352 | } |
| 306 | 353 | |
| 307 | 354 | $fillables = (new CalendarSlot())->getFillable(); |
| 308 | 355 | |
| 309 | - if (in_array($key, $fillables) || isset($bookingEvent->{$key})) { | |
| 356 | + if (in_array($key, $fillables)) { | |
| 310 | 357 | return $bookingEvent->{$key}; |
| 311 | 358 | } |
| 312 | 359 | |
| 313 | - return ''; | |
| 360 | + return self::resolveScalarAttribute($bookingEvent, $key); | |
| 314 | 361 | } |
| 315 | 362 | |
| 316 | 363 | protected static function getCalendarData($key) |
| 317 | 364 | { |
| @@ -322,13 +369,28 @@ | ||
| 322 | 369 | } |
| 323 | 370 | |
| 324 | 371 | $fillables = (new Calendar())->getFillable(); |
| 325 | 372 | |
| 326 | - if (in_array($key, $fillables) || isset($calendar->{$key})) { | |
| 373 | + if (in_array($key, $fillables)) { | |
| 327 | 374 | return $calendar->{$key}; |
| 328 | 375 | } |
| 329 | 376 | |
| 330 | - return ''; | |
| 377 | + return self::resolveScalarAttribute($calendar, $key); | |
| 378 | + } | |
| 379 | + | |
| 380 | + /** | |
| 381 | + * Resolve a scalar attribute only; never a relation (dumps as JSON) or a | |
| 382 | + * dotted hop into a relation's columns. | |
| 383 | + */ | |
| 384 | + protected static function resolveScalarAttribute($model, $key) | |
| 385 | + { | |
| 386 | + if (strpos($key, '.') !== false) { | |
| 387 | + return ''; | |
| 388 | + } | |
| 389 | + | |
| 390 | + $value = isset($model[$key]) ? $model[$key] : ''; | |
| 391 | + | |
| 392 | + return is_scalar($value) ? $value : ''; | |
| 331 | 393 | } |
| 332 | 394 | |
| 333 | 395 | protected static function getPaymentData($key) |
| 334 | 396 | { |