'; if (cvv_mu_cookie_ok()) { echo '
'; echo ''; echo ''; echo ''; echo '
'; } else { $tree_src = '/images/tree.png?vibes=madchill'; echo ''; echo ''; echo ''; } echo ''; } function cvv_echo_site_copyright() { echo ''; } function cvv_echo_site_bottom_block() { echo '
'; cvv_echo_site_copyright(); cvv_echo_site_auth_footer(); echo '
'; } function mup($text = '', $exit = '') { global $userdata, $staging_mode, $advanced_mode; if (mu()) { $backtrace = debug_backtrace(); echo '
'.($exit ? 'Stopping at ' : '').str_replace('staging/', '', str_replace(ROOT_DIR, '', $backtrace[0]['file'])).'  line '.$backtrace[0]['line'].($backtrace[1]['function'] ? '    '.$backtrace[1]['function'].'()' : '').'
'; if (!isset($text)) { if (!$exit) echo 'empty'.$text.'
'; } else { $result = print_r($text, TRUE); if (!is_array($text)) echo '
'.$result.'

'; else echo '
'.$result.'

'; } if ($exit) { global $sitedata; require_once("footer.html"); exit; } } } // ========== Minimal db_connect + coolquery + hotquery ========== $database_connection = null; function db_connect() { global $database_connection; $database_connection = mysqli_connect(CVV_DB_HOST, CVV_DB_USER, CVV_DB_PASS, CVV_DB_NAME); if (!$database_connection) { header('HTTP/1.1 500 Internal Server Error'); $error_msg = mysqli_connect_error(); $error_num = mysqli_connect_errno(); mup("Database connection failed!"); mup("Error #$error_num: $error_msg"); mup("PHP mysqli extension loaded: ".(extension_loaded('mysqli') ? 'YES' : 'NO')); echo 'DB error

Database connection failed!

'; exit; } mysqli_set_charset($database_connection, 'utf8mb4'); } function vsql($s) { global $database_connection; return mysqli_real_escape_string($database_connection, (string) $s); } /** * @param string $sql * @param string|int $onlygetonerow truthy => append LIMIT 1, return one assoc or false */ function coolquery($sql, $onlygetonerow = '') { global $database_connection; if ($onlygetonerow) { $sql .= ' LIMIT 1'; } $res = mysqli_query($database_connection, $sql); if (!$res) { return $onlygetonerow ? false : array(); } if ($onlygetonerow) { $row = mysqli_fetch_assoc($res); mysqli_free_result($res); return $row ? $row : false; } $out = array(); while ($row = mysqli_fetch_assoc($res)) { $out[] = $row; } mysqli_free_result($res); return $out; } function hotquery($query) { global $database_connection; return mysqli_query($database_connection, $query); } /** Relative time helper (from umd.net functions.html). Optional global $user_time_zone (hours from GMT) if set. */ function time_since($mktime, $dont_show_old_times = '', $no_html = '', $all_relative = '', $pure_date = '', $allow_compact = '', $ucfirst = '') { global $user_time_zone; if (isset($user_time_zone)) { $time_zone_corrected_timestamp = $mktime + ($user_time_zone * 3600); $time_zone_corrected_timestamp = $time_zone_corrected_timestamp - date('Z') + (date('I') * 3600); } else { $time_zone_corrected_timestamp = $mktime; } if ($pure_date) { $howold = date('n/j/y', ($time_zone_corrected_timestamp)); $howold_short = $howold; } else { $seconds_since = time() - $mktime; $in = ''; if ($seconds_since < 0) { $in = 'in '; $seconds_since = abs($seconds_since); $ago = ''; } elseif ($all_relative) { $ago = ''; } else { $ago = ' ago'; } if ($seconds_since < 5) { $howold = 'just now'; $howold_short = 'now'; } elseif ($seconds_since < 60) { $rounded_seconds_since = round($seconds_since); $howold = $in.$rounded_seconds_since.' sec'.($rounded_seconds_since != 1 ? 's' : '').$ago; $howold_short = $rounded_seconds_since.'s'; } elseif ($seconds_since < 3600) { $rounded_seconds_since = round(($seconds_since / 60) - .25); $howold = $in.$rounded_seconds_since.' min'.($rounded_seconds_since > 1 ? 's' : '').$ago; $howold_short = $rounded_seconds_since.'m'; } elseif ($seconds_since < 86400) { $rounded_seconds_since = round(($seconds_since / 3600) - .25); $howold = $in.$rounded_seconds_since.' hour'.($rounded_seconds_since > 1 ? 's' : '').$ago; $howold_short = $rounded_seconds_since.'h'; } elseif (is_yesterday($mktime) || is_tomorrow($mktime)) { if ((date('d', $mktime) != date('d')) && !$all_relative) { $rounded_seconds_since = round(($seconds_since / 3600) - .25); if ($in) { $howold = 'tomorrow'; $howold_short = $rounded_seconds_since.'h'; } else { $howold = 'yesterday'; $howold_short = $rounded_seconds_since.'h'; } } else { $howold = $in.ceil($seconds_since / 3600).' hour'.(floor($seconds_since / 3600) > 1 ? 's' : '').$ago; $howold_short = ceil($seconds_since / 3600).'h'; } } elseif ($seconds_since < 518400 && !$all_relative) { $howold = ($in == 'in ' ? 'this ' : '').strtolower(date('l', ($mktime))); $howold_short = substr($howold, 0, 3); } elseif ($seconds_since < 2592000) { $units = $seconds_since / 86400; $floored_units = round($units); $howold = $in.$floored_units.' day'.($floored_units > 1 ? 's' : '').$ago; $howold_short = $floored_units.'d'; } else { if ($all_relative) { if ($seconds_since < 31536000) { $units = $seconds_since / 2592000; $floored_units = floor($units); $howold = $in.$floored_units.' month'.($floored_units > 1 ? 's' : '').$ago; $howold_short = $floored_units.'mo'; } else { $units = $seconds_since / 31536000; $floored_units = floor($units); $howold = $in.$floored_units; if (($units - $floored_units) > .5) { $howold .= ' ½'; $howold .= ' years'.$ago; $howold_short = $floored_units.'.5y'; } else { $howold .= ' year'.($floored_units > 1 ? 's' : '').$ago; $howold_short = $floored_units.'y'; } } } else { if ($dont_show_old_times) { return ''; } else { $howold = date('n/j/y', ($time_zone_corrected_timestamp)); $howold_short = $howold; } } } } if ($ucfirst) { $howold = ucfirst($howold); } if ($no_html) { return $howold; } else { if ($all_relative) { if (!$allow_compact) { return ''.$howold.''; } return ''.$howold.''. ''.$howold_short.''; } if (!$allow_compact) { return ''.$howold.''; } elseif ($allow_compact == 2) { return ''.$howold_short.''; } return ''.$howold.''. ''.$howold_short.''; } } function is_yesterday($mktime) { $twenty_four_hours_ago = time() + (3600) - 86400; $yesterday_test = mktime(0, 0, 0, date('m', $mktime), date('d', $mktime), date('Y', $mktime)); $real_yesterday = mktime(0, 0, 0, date('m', $twenty_four_hours_ago), date('d', $twenty_four_hours_ago), date('Y', $twenty_four_hours_ago)); if ($yesterday_test == $real_yesterday) { return 1; } } function is_tomorrow($mktime) { global $user_time_zone; $tz = isset($user_time_zone) ? (int) $user_time_zone : 0; $in_twenty_four_hours = time() + ($tz * 3600) + 86400; $tomorrow_test = mktime(0, 0, 0, date('m', $mktime), date('d', $mktime), date('Y', $mktime)); $real_tomorrow = mktime(0, 0, 0, date('m', $in_twenty_four_hours), date('d', $in_twenty_four_hours), date('Y', $in_twenty_four_hours)); if ($tomorrow_test == $real_tomorrow) { return 1; } } function cvv_media_ensure_dirs() { $m = __DIR__.'/media'; $t = __DIR__.'/media/thumbs'; if (!is_dir($m)) { @mkdir($m, 0777, true); } if (!is_dir($t)) { @mkdir($t, 0777, true); } @chmod($m, 0777); @chmod($t, 0777); } function cvv_media_thumb_url($basename, $full_web_base, $thumb_web_base) { if (!is_string($basename) || !preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $basename)) { return ''; } $tf = __DIR__.'/media/thumbs/'.$basename; if (is_file($tf)) { return rtrim($thumb_web_base, '/').'/'.$basename; } return rtrim($full_web_base, '/').'/'.$basename; } function cvv_media_delete_stored($basename) { if (!is_string($basename) || !preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $basename)) { return; } $p = __DIR__.'/media/'.$basename; if (is_file($p)) { @unlink($p); } $pt = __DIR__.'/media/thumbs/'.$basename; if (is_file($pt)) { @unlink($pt); } } /** Normalize photos_json: legacy string[] or {"files":[],"main":""}. */ function cvv_photos_normalize($raw_json) { $d = json_decode((string) $raw_json, true); if (!is_array($d)) { return array('files' => array(), 'main' => ''); } if (isset($d['files']) && is_array($d['files'])) { $files = array(); foreach ($d['files'] as $f) { if (is_string($f) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $f)) { $files[] = $f; } } $files = array_values(array_unique($files)); $main = ''; if (isset($d['main']) && is_string($d['main']) && in_array($d['main'], $files, true)) { $main = $d['main']; } if ($main === '' && $files) { $main = $files[0]; } return array('files' => $files, 'main' => $main); } $files = array(); foreach ($d as $v) { if (is_string($v) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $v)) { $files[] = $v; } } $files = array_values(array_unique($files)); return array('files' => $files, 'main' => $files[0] ?? ''); } function cvv_photos_json_encode($files, $main) { $files = array_values(array_unique(is_array($files) ? $files : array())); $main = (string) $main; if ($main === '' || !in_array($main, $files, true)) { $main = $files[0] ?? ''; } return json_encode(array('files' => $files, 'main' => $main)); } function cvv_photos_delete_all_files($files_arr) { if (!is_array($files_arr)) { return; } foreach ($files_arr as $fn) { if (is_string($fn) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $fn)) { cvv_media_delete_stored($fn); } } } function cvv_photos_resolve_main($sel, $photos_final, $photos_new) { $sel = (string) $sel; $photos_new = is_array($photos_new) ? $photos_new : array(); if ($sel === '__first_new__' && $photos_new) { return $photos_new[0]; } if ($sel !== '' && $sel !== '__first__' && $sel !== '__first_new__' && in_array($sel, $photos_final, true)) { return $sel; } return $photos_final[0] ?? ''; } /** vibe_list row: photos_json + legacy image_filename. */ function cvv_vibe_row_photos($row) { if (!is_array($row)) { return array('files' => array(), 'main' => ''); } $pn = cvv_photos_normalize(isset($row['photos_json']) ? $row['photos_json'] : '[]'); if (empty($pn['files']) && !empty($row['image_filename'])) { $fn = (string) $row['image_filename']; if (preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $fn)) { return array('files' => array($fn), 'main' => $fn); } } return $pn; } /** Basenames in images/condo/thumbs that have a matching full image in images/condo. */ function cvv_condo_gallery_list() { $thumb_dir = __DIR__.'/images/condo/thumbs'; $full_dir = __DIR__.'/images/condo'; $out = array(); if (!is_dir($thumb_dir) || !is_dir($full_dir)) { return $out; } $ext_re = '/\.(jpe?g|png|gif|webp)$/i'; foreach (scandir($thumb_dir) as $f) { if ($f === '.' || $f === '..' || strpos($f, '/') !== false || strpos($f, '\\') !== false) { continue; } if (!preg_match($ext_re, $f)) { continue; } if (!is_file($thumb_dir.'/'.$f) || !is_file($full_dir.'/'.$f)) { continue; } $out[] = $f; } natcasesort($out); return array_values($out); } /** Pixel size of square thumbnails on disk (shown at 75px in vibe list). */ define('CVV_THUMB_SQUARE_PX', 150); function cvv_media_write_thumbnail($src_path, $thumb_path, $image_type) { if (!extension_loaded('gd') || !function_exists('imagecreatetruecolor')) { return false; } $info = @getimagesize($src_path); if ($info === false) { return false; } $w = (int) $info[0]; $h = (int) $info[1]; $type = (int) $image_type; if ($w < 1 || $h < 1) { return false; } switch ($type) { case IMAGETYPE_JPEG: $im = @imagecreatefromjpeg($src_path); break; case IMAGETYPE_PNG: $im = @imagecreatefrompng($src_path); break; case IMAGETYPE_GIF: $im = @imagecreatefromgif($src_path); break; case IMAGETYPE_WEBP: if (function_exists('imagecreatefromwebp')) { $im = @imagecreatefromwebp($src_path); } else { return false; } break; default: return false; } if (!$im) { return false; } $S = (int) CVV_THUMB_SQUARE_PX; if ($S < 32) { $S = 75; } $scale = max($S / $w, $S / $h); $cov_w = (int) max(1, round($w * $scale)); $cov_h = (int) max(1, round($h * $scale)); $tmp = imagecreatetruecolor($cov_w, $cov_h); if (!$tmp) { imagedestroy($im); return false; } $is_alpha = ($type === IMAGETYPE_PNG || $type === IMAGETYPE_WEBP || $type === IMAGETYPE_GIF); if ($is_alpha) { imagealphablending($tmp, false); imagesavealpha($tmp, true); $tcol = imagecolorallocatealpha($tmp, 0, 0, 0, 127); imagefill($tmp, 0, 0, $tcol); } else { $bg = imagecolorallocate($tmp, 255, 255, 255); imagefill($tmp, 0, 0, $bg); } imagealphablending($tmp, true); imagecopyresampled($tmp, $im, 0, 0, 0, 0, $cov_w, $cov_h, $w, $h); imagedestroy($im); $sx = (int) max(0, floor(($cov_w - $S) / 2)); $sy = (int) max(0, floor(($cov_h - $S) / 2)); $dst = imagecreatetruecolor($S, $S); if (!$dst) { imagedestroy($tmp); return false; } if ($is_alpha) { imagealphablending($dst, false); imagesavealpha($dst, true); $tcol = imagecolorallocatealpha($dst, 0, 0, 0, 127); imagefill($dst, 0, 0, $tcol); } else { $bg = imagecolorallocate($dst, 255, 255, 255); imagefill($dst, 0, 0, $bg); } imagecopy($dst, $tmp, 0, 0, $sx, $sy, $S, $S); imagedestroy($tmp); $ok = false; switch ($type) { case IMAGETYPE_JPEG: $ok = @imagejpeg($dst, $thumb_path, 88); break; case IMAGETYPE_PNG: $ok = @imagepng($dst, $thumb_path, 6); break; case IMAGETYPE_GIF: $ok = @imagegif($dst, $thumb_path); break; case IMAGETYPE_WEBP: if (function_exists('imagewebp')) { $ok = @imagewebp($dst, $thumb_path, 88); } break; } imagedestroy($dst); return $ok; } function cvv_media_save_upload_with_thumb($tmp) { if (!is_uploaded_file($tmp) || !is_readable($tmp)) { return false; } $info = @getimagesize($tmp); if ($info === false) { return false; } $allowed = array(IMAGETYPE_JPEG => 'jpg', IMAGETYPE_PNG => 'png', IMAGETYPE_GIF => 'gif'); if (defined('IMAGETYPE_WEBP')) { $allowed[IMAGETYPE_WEBP] = 'webp'; } $itype = (int) $info[2]; if (!isset($allowed[$itype])) { return false; } $ext = $allowed[$itype]; $name = bin2hex(random_bytes(12)).'.'.$ext; cvv_media_ensure_dirs(); $dest = __DIR__.'/media/'.$name; if (!@move_uploaded_file($tmp, $dest)) { if (!@copy($tmp, $dest) || !is_file($dest)) { mup(error_get_last(), 1); return false; } } $thumb = __DIR__.'/media/thumbs/'.$name; if (!cvv_media_write_thumbnail($dest, $thumb, $itype)) { @copy($dest, $thumb); } return $name; } function cvv_media_web_base() { global $vibe_self; $d = dirname(str_replace('\\', '/', $vibe_self)); if ($d === '/' || $d === '.' || $d === '') { return '/media'; } return rtrim($d, '/').'/media'; } function cvv_media_thumb_web_base() { global $vibe_self; $d = dirname(str_replace('\\', '/', $vibe_self)); if ($d === '/' || $d === '.' || $d === '') { return '/media/thumbs'; } return rtrim($d, '/').'/media/thumbs'; } db_connect(); function cvv_guestbook_ensure_tables() { global $database_connection; mysqli_query($database_connection, "CREATE TABLE IF NOT EXISTS `cvv_guestbook` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `created_at` DATETIME NOT NULL, `guest_name` VARCHAR(128) NOT NULL, `visit_when` VARCHAR(255) NOT NULL, `story` TEXT NOT NULL, `photos_json` TEXT NOT NULL, `thedate` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `ix_created` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); cvv_guestbook_ensure_thedate_column(); cvv_guestbook_ensure_thestatus_column(); } function cvv_guestbook_ensure_thestatus_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `cvv_guestbook` LIKE 'thestatus'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `cvv_guestbook` ADD COLUMN `thestatus` TINYINT(1) NOT NULL DEFAULT 1"); } function cvv_guestbook_ensure_thedate_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `cvv_guestbook` LIKE 'thedate'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `cvv_guestbook` ADD COLUMN `thedate` INT UNSIGNED NOT NULL DEFAULT 0"); } /** Guestbook `thestatus`: 1 = approved (visible to public), 0 = pending. */ function cvv_guestbook_row_is_approved($row) { if (!is_array($row)) { return false; } return ((int) ($row['thestatus'] ?? 1)) === 1; } /** Notify when an entry moves from pending to approved (owner saved with Approved checked). */ function cvv_guestbook_mail_approval_notice($entry_id, $guest_name, $visit_when) { $to = 'derek@darkcolors.com'; $subject = 'Chill Vibes Villa: Guestbook entry approved'; $guest_name = trim((string) $guest_name); $visit_when = trim((string) $visit_when); $body = "A guestbook entry was just approved on Chill Vibes Villa.\r\n\r\n"; $body .= 'Entry ID: '.intval($entry_id)."\r\n"; $body .= 'Guest: '.$guest_name."\r\n"; $body .= 'Stayed: '.$visit_when."\r\n"; $body .= "\r\nOpen the site Guestbook tab (while signed in) to read the full post if needed.\r\n"; $from = 'Chill Vibes Villa '; $headers = 'From: '.$from."\r\nContent-Type: text/plain; charset=UTF-8"; @mail($to, $subject, $body, $headers); } function cvv_vibe_list_ensure_image_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `vibe_list` LIKE 'image_filename'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `vibe_list` ADD COLUMN `image_filename` VARCHAR(64) NOT NULL DEFAULT ''"); } function cvv_vibe_list_ensure_photos_json_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `vibe_list` LIKE 'photos_json'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `vibe_list` ADD COLUMN `photos_json` TEXT NOT NULL DEFAULT ''"); } function cvv_vibe_list_ensure_website_url_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `vibe_list` LIKE 'website_url'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `vibe_list` ADD COLUMN `website_url` VARCHAR(512) NOT NULL DEFAULT ''"); } function cvv_vibe_list_ensure_distance_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `vibe_list` LIKE 'distance'"); if ($res && mysqli_num_rows($res) > 0) { $row = mysqli_fetch_assoc($res); mysqli_free_result($res); $type = isset($row['Type']) ? strtolower($row['Type']) : ''; if (preg_match('/^decimal\((\d+),(\d+)\)/', $type, $m)) { $scale = (int) $m[2]; if ($scale < 2) { @mysqli_query($database_connection, "ALTER TABLE `vibe_list` MODIFY COLUMN `distance` DECIMAL(8,2) NOT NULL DEFAULT 0"); } } return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `vibe_list` ADD COLUMN `distance` DECIMAL(8,2) NOT NULL DEFAULT 0"); } function cvv_vibe_list_ensure_thestatus_column() { global $database_connection; $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `vibe_list` LIKE 'thestatus'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); return; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `vibe_list` ADD COLUMN `thestatus` TINYINT(1) NOT NULL DEFAULT 1"); } /** vibe_list `thestatus`: 1 = approved (visible to public), 0 = pending. */ function cvv_vibe_list_row_is_approved($row) { if (!is_array($row)) { return false; } return ((int) ($row['thestatus'] ?? 1)) === 1; } function cvv_vibe_list_ensure_bool_columns($bool_fields) { global $database_connection; foreach ($bool_fields as $col) { if (!is_string($col) || !preg_match('/^[a-z][a-z0-9_]*$/', $col)) { continue; } $esc = mysqli_real_escape_string($database_connection, $col); $res = @mysqli_query($database_connection, "SHOW COLUMNS FROM `vibe_list` LIKE '$esc'"); if ($res && mysqli_num_rows($res) > 0) { mysqli_free_result($res); continue; } if ($res) { mysqli_free_result($res); } @mysqli_query($database_connection, "ALTER TABLE `vibe_list` ADD COLUMN `".$col."` TINYINT(1) NOT NULL DEFAULT 0"); } } $userdata = array('permissions' => array('manage_directory' => array(1 => true))); $sitedata = array('id' => 1, 'rue' => ''); $cvv_script = basename($_SERVER['SCRIPT_NAME'] ?? 'index.html'); $cvv_dir = dirname(str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '/')); $vibe_self = ($cvv_dir === '/' || $cvv_dir === '.') ? '/'.$cvv_script : rtrim($cvv_dir, '/').'/'.$cvv_script; if (isset($vibe_self[0]) && $vibe_self[0] !== '/') { $vibe_self = '/'.$vibe_self; } /** Clean URL base (no .html), e.g. /index → https://chillvibesvilla.com/index/guestbook */ $cvv_dir_slash = dirname(str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '/')); $CVV_CLEAN_BASE = ($cvv_dir_slash === '/' || $cvv_dir_slash === '.') ? '/index' : rtrim($cvv_dir_slash, '/').'/index'; /** * Tab URLs: /index (vibes), /index/guestbook, /index/info. Optional: /index/vibes. */ function cvv_url_for_tab($tab) { global $CVV_CLEAN_BASE; $tab = strtolower(trim((string) $tab)); if ($tab === 'vibes' || $tab === '' || $tab === 'vibez') { return $CVV_CLEAN_BASE; } if ($tab === 'guestbook' || $tab === 'info') { return $CVV_CLEAN_BASE.'/'.$tab; } return $CVV_CLEAN_BASE; } /** * Map REQUEST_URI to tab: primary /index/{guestbook|info|vibes}; legacy /guestbook and index.html paths. */ function cvv_path_tab_from_request() { global $CVV_CLEAN_BASE; $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); $path = '/' . trim((string) $path, '/'); $base = $CVV_CLEAN_BASE; if ($base === '' || (isset($base[0]) && $base[0] !== '/')) { $base = '/' . ltrim($base, '/'); } $base = rtrim($base, '/'); if ($path === $base || $path === $base . '/') { return 'vibes'; } $prefix = $base . '/'; if (strpos($path, $prefix) === 0) { $rest = substr($path, strlen($prefix)); $seg = strtolower(trim(explode('/', $rest)[0], '/')); if ($seg === 'guestbook') { return 'guestbook'; } if ($seg === 'info') { return 'info'; } if ($seg === 'vibes' || $seg === 'vibez') { return 'vibes'; } return ''; } $path_trim = trim($path, '/'); if ($path_trim === '') { return ''; } $segs = explode('/', $path_trim); $last = strtolower(end($segs)); if ($last === 'index.html' || preg_match('/\.html$/i', $last)) { if (count($segs) >= 2) { $prev = strtolower($segs[count($segs) - 2]); if ($prev === 'guestbook' || $prev === 'info' || $prev === 'vibes' || $prev === 'vibez') { return $prev === 'vibez' ? 'vibes' : $prev; } } return ''; } if ($last === 'guestbook' || $last === 'info' || $last === 'vibes' || $last === 'vibez') { return $last === 'vibez' ? 'vibes' : $last; } return ''; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['cvv_site_logout'])) { $secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; setcookie(CVV_MU_COOKIE, '', time() - 3600, '/', '', $secure, true); $redir = cvv_url_for_tab('vibes'); $ar = $_POST['cvv_auth_return'] ?? ''; if (is_string($ar) && $ar !== '' && isset($ar[0]) && $ar[0] === '/') { $redir = $ar; } header('Location: '.$redir); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['cvv_site_auth'])) { $pw = (string) ($_POST['cvv_site_pw'] ?? ''); $secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; if (hash_equals(CVV_MU_PASS, $pw)) { setcookie(CVV_MU_COOKIE, CVV_MU_PASS, time() + 31536000, '/', '', $secure, true); } else { setcookie(CVV_MU_COOKIE, '', time() - 3600, '/', '', $secure, true); } $redir = cvv_url_for_tab('vibes'); $ar = $_POST['cvv_auth_return'] ?? ''; if (is_string($ar) && $ar !== '' && isset($ar[0]) && $ar[0] === '/') { $redir = $ar; } header('Location: '.$redir); exit; } /** Allowed `vibe_list.category` values (varchar: store this exact string). */ $cvv_vibe_categories = array( 'restaurants / bars', 'attractions', 'shopping', ); /** Legacy tinyint IDs still in DB until rows are updated. */ function cvv_vibe_category_legacy_id_to_name($n) { $map = array( 1 => 'restaurants / bars', 2 => 'attractions', 3 => 'shopping', ); $n = (int) $n; return isset($map[$n]) ? $map[$n] : ''; } /** * Return canonical whitelist string for DB/display, or '' if n/a / unknown POST. * Accepts legacy numeric 1–3 from old schema. */ function cvv_vibe_category_normalize($raw) { global $cvv_vibe_categories; if ($raw === '' || $raw === null) { return ''; } $s = trim((string) $raw); if ($s === '') { return ''; } if (ctype_digit($s)) { $legacy = cvv_vibe_category_legacy_id_to_name((int) $s); if ($legacy !== '' && in_array($legacy, $cvv_vibe_categories, true)) { return $legacy; } return ''; } $sl = strtolower($s); foreach ($cvv_vibe_categories as $allowed) { if (strtolower($allowed) === $sl) { return $allowed; } } return ''; } $addnew = !empty($_GET['addnew']); $vibe_id = isset($_GET['vibe_id']) ? intval($_GET['vibe_id']) : 0; $delete_id = isset($_GET['delete']) ? intval($_GET['delete']) : 0; $confirm_delete = !empty($_GET['confirm']); $return = isset($_GET['return']) ? (string) $_GET['return'] : cvv_url_for_tab('vibes'); if ($return === '' || $return[0] !== '/') { $return = cvv_url_for_tab('vibes'); } $gb_delete_id = isset($_GET['gbdel']) ? intval($_GET['gbdel']) : 0; $gb_confirm = !empty($_GET['gbconfirm']); $gb_edit_id_get = isset($_GET['gbedit']) ? intval($_GET['gbedit']) : 0; $gb_edit_row = null; if (mu() && $gb_edit_id_get > 0) { cvv_guestbook_ensure_tables(); $gb_edit_row = coolquery("SELECT * FROM `cvv_guestbook` WHERE id='".$gb_edit_id_get."'", 1); if (!is_array($gb_edit_row)) { $gb_edit_row = null; $gb_edit_id_get = 0; } } if (mu() && $gb_delete_id && $gb_confirm) { cvv_guestbook_ensure_tables(); $row = coolquery("SELECT photos_json FROM `cvv_guestbook` WHERE id='".$gb_delete_id."'", 1); if (is_array($row)) { $pn = cvv_photos_normalize($row['photos_json'] ?? '[]'); cvv_photos_delete_all_files($pn['files']); hotquery("DELETE FROM `cvv_guestbook` WHERE id='".$gb_delete_id."' LIMIT 1"); } header('Location: '.cvv_url_for_tab('guestbook')); exit; } $guestbook_err = ''; if (isset($_POST['guestbook_save'])) { if (mu()) { cvv_guestbook_ensure_tables(); } if (!empty($_POST['gb_company'])) { header('Location: '.cvv_url_for_tab('guestbook')); exit; } $gname = trim((string) ($_POST['gb_name'] ?? '')); $gwhen = trim((string) ($_POST['gb_when'] ?? '')); $gstory = trim((string) ($_POST['gb_story'] ?? '')); $slen = function_exists('mb_strlen') ? 'mb_strlen' : 'strlen'; if ($gname === '' || $slen($gname) > 120) { $guestbook_err = 'Please enter your name (120 characters or fewer).'; } elseif ($gwhen === '' || $slen($gwhen) > 255) { $guestbook_err = 'Please say when you stayed (255 characters or fewer).'; } elseif ($gstory === '' || $slen($gstory) > 12000) { $guestbook_err = 'Please share a short story about your visit (12,000 characters or fewer).'; } else { cvv_media_ensure_dirs(); $photos_saved = array(); $max_photos = 5; $max_bytes = 10 * 1024 * 1024; global $database_connection; $gb_entry_id = isset($_POST['gb_entry_id']) ? intval($_POST['gb_entry_id']) : 0; if ($gb_entry_id > 0 && !mu()) { $gb_entry_id = 0; } $gb_is_update = ($gb_entry_id > 0 && mu()); if ($gb_is_update) { $gb_existing = coolquery("SELECT * FROM `cvv_guestbook` WHERE id='".$gb_entry_id."'", 1); if (!is_array($gb_existing)) { $guestbook_err = 'Could not find that guestbook entry.'; } else { $photos_kept = array(); $pn_existing = cvv_photos_normalize($gb_existing['photos_json'] ?? '[]'); $photos_existing = $pn_existing['files']; $del_raw = $_POST['gb_photo_delete'] ?? array(); if (!is_array($del_raw)) { $del_raw = array($del_raw); } foreach ($photos_existing as $pfn) { if (!is_string($pfn) || !preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $pfn)) { continue; } if (in_array($pfn, $del_raw, true)) { cvv_media_delete_stored($pfn); continue; } $photos_kept[] = $pfn; } $photos_final = $photos_kept; $photos_new_upload = array(); if (!empty($_FILES['gb_photos']) && is_array($_FILES['gb_photos']['tmp_name'])) { $n = count($_FILES['gb_photos']['tmp_name']); for ($i = 0; $i < $n && count($photos_final) < $max_photos; $i++) { if (empty($_FILES['gb_photos']['tmp_name'][$i])) { continue; } if ($_FILES['gb_photos']['error'][$i] !== UPLOAD_ERR_OK) { if ($_FILES['gb_photos']['error'][$i] !== UPLOAD_ERR_NO_FILE) { $guestbook_err = 'One of the uploads failed. Try again with smaller images.'; } break; } if ($_FILES['gb_photos']['size'][$i] > $max_bytes) { $guestbook_err = 'Each photo must be 4MB or smaller.'; break; } $saved = cvv_media_save_upload_with_thumb($_FILES['gb_photos']['tmp_name'][$i]); if ($saved === false) { $guestbook_err = 'Photos must be JPEG, PNG, GIF, or WebP.'; break; } $photos_final[] = $saved; $photos_saved[] = $saved; $photos_new_upload[] = $saved; } } if ($guestbook_err === '') { $gb_main = (string) ($_POST['gb_main'] ?? '__first__'); $main_fn = cvv_photos_resolve_main($gb_main, $photos_final, $photos_new_upload); $gj = vsql(cvv_photos_json_encode($photos_final, $main_fn)); $gname_ = vsql(substr($gname, 0, 120)); $gwhen_ = vsql(substr($gwhen, 0, 255)); $gstory_ = vsql($gstory); $gb_thestatus = !empty($_POST['gb_approved']) ? 1 : 0; $gb_prev_status = (int) ($gb_existing['thestatus'] ?? 1); $upd_sql = "UPDATE `cvv_guestbook` SET guest_name='$gname_', visit_when='$gwhen_', story='$gstory_', photos_json='$gj', thestatus='".intval($gb_thestatus)."' WHERE id='".$gb_entry_id."' LIMIT 1"; if (mysqli_query($database_connection, $upd_sql)) { if ($gb_prev_status === 0 && $gb_thestatus === 1) { cvv_guestbook_mail_approval_notice($gb_entry_id, $gname, $gwhen); } header('Location: '.cvv_url_for_tab('guestbook').'?updated=1'); exit; } $guestbook_err = 'Could not save guestbook entry. '.mysqli_error($database_connection); } if ($guestbook_err !== '') { foreach ($photos_saved as $fn) { if (is_string($fn) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $fn)) { cvv_media_delete_stored($fn); } } } } } else { if (!empty($_FILES['gb_photos']) && is_array($_FILES['gb_photos']['tmp_name'])) { $n = count($_FILES['gb_photos']['tmp_name']); for ($i = 0; $i < $n && count($photos_saved) < $max_photos; $i++) { if (empty($_FILES['gb_photos']['tmp_name'][$i])) { continue; } if ($_FILES['gb_photos']['error'][$i] !== UPLOAD_ERR_OK) { if ($_FILES['gb_photos']['error'][$i] !== UPLOAD_ERR_NO_FILE) { $guestbook_err = 'One of the uploads failed. Try again with smaller images.'; } break; } if ($_FILES['gb_photos']['size'][$i] > $max_bytes) { $guestbook_err = 'Each photo must be 4MB or smaller.'; break; } $saved = cvv_media_save_upload_with_thumb($_FILES['gb_photos']['tmp_name'][$i]); if ($saved === false) { $guestbook_err = 'Photos must be JPEG, PNG, GIF, or WebP.'; break; } $photos_saved[] = $saved; } } if ($guestbook_err === '') { $gb_main = (string) ($_POST['gb_main'] ?? '__first__'); $main_fn = cvv_photos_resolve_main($gb_main, $photos_saved, $photos_saved); $gj = vsql(cvv_photos_json_encode($photos_saved, $main_fn)); $gname_ = vsql(substr($gname, 0, 120)); $gwhen_ = vsql(substr($gwhen, 0, 255)); $gstory_ = vsql($gstory); $now = date('Y-m-d H:i:s'); $gb_thedate = time(); $gb_thestatus_ins = (mu() && !empty($_POST['gb_approved'])) ? 1 : 0; $ins_sql = "INSERT INTO `cvv_guestbook` (created_at, guest_name, visit_when, story, photos_json, thedate, thestatus) VALUES ('$now', '$gname_', '$gwhen_', '$gstory_', '$gj', ".intval($gb_thedate).", ".intval($gb_thestatus_ins).")"; if (mysqli_query($database_connection, $ins_sql)) { header('Location: '.cvv_url_for_tab('guestbook').'?posted=1'); exit; } $guestbook_err = 'Could not save guestbook entry. '.mysqli_error($database_connection); } if ($guestbook_err !== '') { foreach ($photos_saved as $fn) { if (is_string($fn) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $fn)) { cvv_media_delete_stored($fn); } } } } } } if ($guestbook_err !== '' && mu()) { $eid = intval($_POST['gb_entry_id'] ?? 0); if ($eid > 0) { cvv_guestbook_ensure_tables(); $row_reload = coolquery("SELECT * FROM `cvv_guestbook` WHERE id='".$eid."'", 1); if (is_array($row_reload)) { $gb_edit_row = $row_reload; } } } // --- vibe_list (same as test.html) --- $vibe_list_saved = ''; $vibe_list_bool_fields = array( 'rooftop_dining', 'outdoor_dining', 'oceanfront', 'for_families', 'live_music', 'djays', 'lounge', 'karaoke', 'brunch', 'breakfast', 'late_night_food', 'coffee_shops', 'blue_crabs', 'rainy_day', 'action_packed', ); $vibe_list_crab_sql = '`crab legs`'; if (mu()) { cvv_vibe_list_ensure_image_column(); cvv_vibe_list_ensure_photos_json_column(); cvv_vibe_list_ensure_website_url_column(); cvv_vibe_list_ensure_distance_column(); cvv_vibe_list_ensure_thestatus_column(); cvv_vibe_list_ensure_bool_columns($vibe_list_bool_fields); } $cvv_vibe_rating_words = array( 1 => 'quirky', 2 => 'swanky', 3 => 'festive', 4 => 'laid back', 5 => 'mad chill', 7 => 'upscale', 8 => 'dive-y', 13 => 'rustic', 15 => 'intimate', 20 => 'energetic', ); $cvv_overall_rating_words = array( 1 => 'great', 2 => 'really great', 3 => 'excellent', 4 => 'elite', 5 => 'mind-blowing', ); // Handle delete if ($delete_id && $confirm_delete) { $del_row = coolquery("SELECT image_filename, photos_json FROM `vibe_list` WHERE id = '".intval($delete_id)."'", 1); if (is_array($del_row)) { $pn = cvv_vibe_row_photos($del_row); cvv_photos_delete_all_files($pn['files']); } hotquery("DELETE FROM `vibe_list` WHERE id = '".intval($delete_id)."' LIMIT 1"); header('Location: '.$return); exit; } if (isset($_POST['vibe_list_save'])) { global $database_connection; $vibe_post_id = isset($_POST['vibe_id']) ? intval($_POST['vibe_id']) : 0; $vibe_name = vsql(substr($_POST['name'] ?? '', 0, 64)); $cat_post_raw = isset($_POST['category']) ? trim((string) $_POST['category']) : ''; $vibe_cat_norm = cvv_vibe_category_normalize($cat_post_raw); if ($vibe_cat_norm === '' && !empty($cvv_vibe_categories)) { $vibe_cat_norm = $cvv_vibe_categories[0]; } $vibe_category_sql = vsql(substr($vibe_cat_norm, 0, 128)); $vibe_address = vsql($_POST['address'] ?? ''); $raw_web = trim((string) ($_POST['website_url'] ?? '')); if ($raw_web !== '') { if (!preg_match('#^https?://#i', $raw_web)) { $raw_web = 'https://'.$raw_web; } if (!preg_match('#^https?://\\S+#i', $raw_web)) { $raw_web = ''; } } $vibe_website_sql = vsql(substr($raw_web, 0, 512)); $dist_raw = trim((string) ($_POST['distance'] ?? '')); $vibe_dist = 0.0; if ($dist_raw !== '' && is_numeric($dist_raw)) { $vibe_dist = max(0.0, min(9999.99, round((float) $dist_raw, 2))); } $vibe_distance_sql = vsql(number_format($vibe_dist, 2, '.', '')); $vibe_summary = vsql($_POST['summary'] ?? ''); $vibe_review = vsql($_POST['review'] ?? ''); $vibe_cost = vsql(substr($_POST['cost_level'] ?? '', 0, 1)); $vr_raw = (string) ($_POST['vibe_rating'] ?? ''); $vr_num = cvv_vibe_rating_num_from_raw($vr_raw, $cvv_vibe_rating_words); $vibe_vrating = ($vr_num !== null) ? vsql((string) $vr_num) : ''; $or_raw = (string) ($_POST['overall_rating'] ?? ''); $or_num = cvv_vibe_rating_num_from_raw($or_raw, $cvv_overall_rating_words); $vibe_orating = ($or_num !== null) ? vsql((string) $or_num) : ''; $vibe_crab = !empty($_POST['crab_legs']) ? 1 : 0; $max_photos = 5; $max_bytes = 4 * 1024 * 1024; $photos_saved = array(); $photos_new_upload = array(); $photos_kept = array(); if ($vibe_post_id) { $cur_img = coolquery("SELECT image_filename, photos_json FROM `vibe_list` WHERE id='".$vibe_post_id."'", 1); $pn_cur = cvv_vibe_row_photos(is_array($cur_img) ? $cur_img : array()); $photos_existing = $pn_cur['files']; $del_raw = $_POST['vibe_photo_delete'] ?? array(); if (!is_array($del_raw)) { $del_raw = array($del_raw); } foreach ($photos_existing as $pfn) { if (!is_string($pfn) || !preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $pfn)) { continue; } if (in_array($pfn, $del_raw, true)) { cvv_media_delete_stored($pfn); continue; } $photos_kept[] = $pfn; } } $photos_final = $photos_kept; if (!empty($_FILES['vibe_photos']) && is_array($_FILES['vibe_photos']['tmp_name'])) { cvv_media_ensure_dirs(); $n = count($_FILES['vibe_photos']['tmp_name']); for ($i = 0; $i < $n && count($photos_final) < $max_photos; $i++) { if (empty($_FILES['vibe_photos']['tmp_name'][$i])) { continue; } if ($_FILES['vibe_photos']['error'][$i] !== UPLOAD_ERR_OK) { if ($_FILES['vibe_photos']['error'][$i] !== UPLOAD_ERR_NO_FILE) { break; } continue; } if ($_FILES['vibe_photos']['size'][$i] > $max_bytes) { break; } $saved = cvv_media_save_upload_with_thumb($_FILES['vibe_photos']['tmp_name'][$i]); if ($saved === false) { break; } $photos_final[] = $saved; $photos_saved[] = $saved; $photos_new_upload[] = $saved; } } $vibe_main = (string) ($_POST['vibe_main'] ?? '__first__'); $main_fn = cvv_photos_resolve_main($vibe_main, $photos_final, $photos_new_upload); $photos_json_sql = vsql(cvv_photos_json_encode($photos_final, $main_fn)); $img_fn_sql = vsql(substr($main_fn, 0, 64)); $sets = array(); foreach ($vibe_list_bool_fields as $bf) { $sets[] = "`$bf`='".(isset($_POST[$bf]) && $_POST[$bf] !== '' && $_POST[$bf] !== '0' ? 1 : 0)."'"; } $sets[] = "$vibe_list_crab_sql='$vibe_crab'"; $bool_sql = implode(', ', $sets); $vibe_status_sql = ''; if (mu()) { $vibe_ts = !empty($_POST['vibe_approved']) ? 1 : 0; $vibe_status_sql = ", thestatus='".intval($vibe_ts)."'"; } if ($vibe_post_id) { $q = "UPDATE `vibe_list` SET name='$vibe_name', category='".$vibe_category_sql."', address='$vibe_address', website_url='$vibe_website_sql', distance='".$vibe_distance_sql."', summary='$vibe_summary', review='$vibe_review', $bool_sql, cost_level='$vibe_cost', vibe_rating='$vibe_vrating', overall_rating='$vibe_orating', image_filename='$img_fn_sql', photos_json='$photos_json_sql'".$vibe_status_sql." WHERE id='".$vibe_post_id."' LIMIT 1"; if (!mysqli_query($database_connection, $q)) { error_log('cvv vibe_list UPDATE failed: '.mysqli_error($database_connection)); foreach ($photos_saved as $fn) { if (is_string($fn) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $fn)) { cvv_media_delete_stored($fn); } } if (mu()) { header('Location: '.cvv_url_for_tab('vibes').'?vibe_id='.intval($vibe_post_id).'&save_err=1'); } else { header('Location: '.cvv_url_for_tab('vibes')); } exit; } $vibe_id = $vibe_post_id; } else { $next = coolquery("SELECT COALESCE(MAX(id), 0) + 1 AS n FROM `vibe_list`", 1); $new_id = $next ? intval($next['n']) : 1; $vals_bool = array(); foreach ($vibe_list_bool_fields as $bf) { $vals_bool[] = "'".(isset($_POST[$bf]) && $_POST[$bf] !== '' && $_POST[$bf] !== '0' ? 1 : 0)."'"; } $vals_bool[] = "'$vibe_crab'"; $vibe_thestatus_ins = (mu() && !empty($_POST['vibe_approved'])) ? 1 : 0; $q = "INSERT INTO `vibe_list` (id, name, category, address, website_url, distance, summary, review, ".implode(', ', array_map(function ($f) { return "`$f`"; }, $vibe_list_bool_fields)).", $vibe_list_crab_sql, cost_level, vibe_rating, overall_rating, image_filename, photos_json, thestatus) VALUES ('$new_id', '$vibe_name', '".$vibe_category_sql."', '$vibe_address', '$vibe_website_sql', '".$vibe_distance_sql."', '$vibe_summary', '$vibe_review', ".implode(', ', $vals_bool).", '$vibe_cost', '$vibe_vrating', '$vibe_orating', '$img_fn_sql', '$photos_json_sql', '".intval($vibe_thestatus_ins)."')"; if (!mysqli_query($database_connection, $q)) { error_log('cvv vibe_list INSERT failed: '.mysqli_error($database_connection)); foreach ($photos_saved as $fn) { if (is_string($fn) && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $fn)) { cvv_media_delete_stored($fn); } } if (mu()) { header('Location: '.cvv_url_for_tab('vibes').'?addnew=1&save_err=1'); } else { header('Location: '.cvv_url_for_tab('vibes')); } exit; } $vibe_id = $new_id; } $redir = isset($_POST['return']) ? (string) $_POST['return'] : cvv_url_for_tab('vibes'); if ($redir === '' || $redir[0] !== '/') { $redir = cvv_url_for_tab('vibes'); } header('Location: '.$redir); exit; } $vibe_row = array(); if (!empty($vibe_id)) { $vibe_row = coolquery("SELECT * FROM `vibe_list` WHERE id = '".intval($vibe_id)."'", 1); } if (!is_array($vibe_row)) { $vibe_row = array(); } function vibe_list_website_href($stored) { $u = trim((string) $stored); if ($u === '') { return ''; } if (!preg_match('#^https?://#i', $u)) { $u = 'https://'.$u; } if (!preg_match('#^https?://\\S+#i', $u)) { return ''; } return $u; } function vibe_list_crab_val($r) { if (isset($r['crab legs'])) { return $r['crab legs']; } if (isset($r['crab_legs'])) { return $r['crab_legs']; } return ''; } function vibe_list_chk($r, $k) { if (!is_array($r) || !array_key_exists($k, $r)) { return ''; } return ((int) $r[$k] !== 0) ? ' checked' : ''; } function cvv_vibe_rating_num_from_raw($raw, $words_map) { global $cvv_vibe_rating_words; $s = trim((string) $raw); if ($s === '') { return null; } if (ctype_digit($s)) { $n = (int) $s; if (isset($words_map[$n])) { return $n; } return null; } $sl = strtolower($s); foreach ($words_map as $n => $w) { if (strtolower((string) $w) === $sl) { return (int) $n; } } if ($words_map === $cvv_vibe_rating_words) { $legacy = array( 'solid' => 2, 'thorough' => 3, 'elevated' => 4, 'immaculate' => 5, ); if (isset($legacy[$sl])) { return (int) $legacy[$sl]; } } return null; } /** 1–5 (or label text) → 1–10 scale for trees and sort; legacy 1–10 decimals still supported. */ function cvv_vibe_rating_to_ten($raw, $words_map) { $s = trim(str_replace(',', '.', (string) $raw)); if ($s === '') { return 0.0; } $n = cvv_vibe_rating_num_from_raw($s, $words_map); if ($n !== null) { return (float) $n * 2.0; } if (preg_match('/^(10|[1-9])(\.5)?$/', $s)) { return (float) $s; } return 0.0; } function cvv_vibe_stars_html($rating_raw, $overall_words_map) { $ten = cvv_vibe_rating_to_ten($rating_raw, $overall_words_map); if ($ten <= 0) { return ''; } if ($ten > 10) { $ten = 10.0; } $stars = $ten / 2.0; $lbl = ($ten == floor($ten)) ? (string) (int) $ten : rtrim(rtrim(sprintf('%.1f', $ten), '0'), '.'); $label = 'Our rating '.$lbl.' out of 10'; $tree = '/images/tree.png?vibes=madchill'; $out = ''; for ($i = 1; $i <= 5; $i++) { if ($stars >= $i) { $out .= ''; } elseif ($stars >= $i - 0.5) { $out .= ''; } else { $out .= ''; } } $out .= ''; return $out; } function cvv_vibe_rating_word_label($words_map, $raw) { $n = cvv_vibe_rating_num_from_raw($raw, $words_map); if ($n !== null && isset($words_map[$n])) { return $words_map[$n]; } return ''; } $labels = array( 'rooftop_dining' => 'rooftop', 'outdoor_dining' => 'outdoor dining', 'oceanfront' => 'oceanfront', 'for_families' => 'for families', 'live_music' => 'live music', 'djays' => 'djs', 'lounge' => 'lounge', 'karaoke' => 'karaoke', 'brunch' => 'brunch', 'breakfast' => 'breakfast', 'late_night_food' => 'late night food', 'coffee_shops' => 'coffee shops', 'blue_crabs' => 'blue crabs', 'rainy_day' => 'rainy day', 'action_packed' => 'action-packed', 'crab_legs' => 'crab legs', ); $current_category = cvv_vibe_category_normalize($vibe_row['category'] ?? ''); ?> Chill Vibes Villa '; echo '
Delete guestbook entry
'; echo '
Remove this memory from '.($gb_row['guest_name'] ?? 'Unknown').'?
'; $gb_yes = cvv_url_for_tab('guestbook').'?gbdel='.$gb_delete_id.'&gbconfirm=1'; echo 'Yes, delete '; echo 'Cancel'; echo ''; cvv_echo_site_bottom_block(); echo ''; exit; } // Delete confirmation if ($delete_id && !$confirm_delete) { $del_item = coolquery("SELECT name FROM `vibe_list` WHERE id = '".intval($delete_id)."'", 1); echo '
'; echo '
Delete Confirmation
'; echo '
Are you sure you want to delete "'.($del_item['name'] ?? 'Unknown').'"?
'; $del_url = cvv_url_for_tab('vibes').'?delete='.intval($delete_id).'&confirm=1&return='.rawurlencode($return); echo 'Yes, Delete '; echo 'Cancel'; echo '
'; cvv_echo_site_bottom_block(); echo ''; exit; } echo '
Chill Vibes Villa
'; if ($addnew) { echo '
'; echo '
Vibez List
'; if (mu() && !empty($_GET['save_err'])) { echo '
Could not save this entry. If it keeps happening, check the server PHP error log.
'; } echo '
'; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; $cvv_dist_form = ''; if (isset($vibe_row['distance']) && $vibe_row['distance'] !== '' && $vibe_row['distance'] !== null && is_numeric($vibe_row['distance']) && (float) $vibe_row['distance'] > 0) { $cvv_d = (float) $vibe_row['distance']; $cvv_dist_form = (floor($cvv_d) == $cvv_d) ? (string) (int) $cvv_d : rtrim(rtrim(sprintf('%F', $cvv_d), '0'), '.'); } echo ''; echo ''; echo ''; echo ''; $vibe_prev_norm = cvv_vibe_row_photos($vibe_row); $vibe_prev_files = $vibe_prev_norm['files']; $vibe_prev_main = $vibe_prev_norm['main']; $vibe_preview_web = cvv_media_web_base(); $vibe_preview_thumb = cvv_media_thumb_web_base(); echo ''; echo ''; echo ''; echo ''; echo '
Name
Category'; $cvv_cat_i = 0; foreach ($cvv_vibe_categories as $cvv_cat_label) { $checked = (($current_category === $cvv_cat_label) || ($current_category === '' && $cvv_cat_i === 0)) ? ' checked' : ''; $cvv_cat_i++; echo ''; } echo '
Address
Website
Distance miles
Summary
Review
Tags'; foreach ($vibe_list_bool_fields as $bf) { echo ' '; } echo ''; echo '
Photos'; if ($vibe_prev_files) { echo '
Pick a main image for the card (up to 5 total)
'; echo '
'; foreach ($vibe_prev_files as $vpfn) { if (!is_string($vpfn) || !preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $vpfn)) { continue; } $preview_src = cvv_media_thumb_url($vpfn, $vibe_preview_web, $vibe_preview_thumb); $vm_chk = ($vpfn === $vibe_prev_main) ? ' checked' : ''; echo '
'; echo ''; echo ''; echo ''; echo '
'; } echo '
'; echo '
'; } echo '
'; echo '
Cost level'; $cur_cost = (string) ($vibe_row['cost_level'] ?? ''); $cost_labels = array(1 => '$', 2 => '$$', 3 => '$$$', 4 => '$$$$', 5 => '$$$$$'); for ($rc = 1; $rc <= 5; $rc++) { $rc_chk = ($cur_cost === (string) $rc) ? ' checked' : ''; echo ''; } echo '
Vibe'; $cur_vr = trim((string) ($vibe_row['vibe_rating'] ?? '')); $vr_resolved = cvv_vibe_rating_num_from_raw($cur_vr, $cvv_vibe_rating_words); $vr_na = ($vr_resolved === null); echo ''; foreach ($cvv_vibe_rating_words as $rv => $rv_lbl) { $rv_chk = ($vr_resolved !== null && (int) $rv === $vr_resolved) ? ' checked' : ''; echo ''; } echo '
Our rating'; $cur_or = trim((string) ($vibe_row['overall_rating'] ?? '')); $or_resolved = cvv_vibe_rating_num_from_raw($cur_or, $cvv_overall_rating_words); if ($or_resolved === null) { $or_resolved = 1; } foreach ($cvv_overall_rating_words as $ro => $ro_lbl) { $ro_chk = ($or_resolved !== null && (int) $ro === $or_resolved) ? ' checked' : ''; echo ''; } echo '
'; $vibe_approved_checked = false; if (mu()) { if (!empty($_GET['save_err'])) { $vibe_approved_checked = !empty($_POST['vibe_approved']); } elseif (!empty($vibe_row['id'])) { $vibe_approved_checked = cvv_vibe_list_row_is_approved($vibe_row); } } echo ''; if (mu()) { echo ''; } $vibe_btn_val = ($vibe_row['id'] ?? '') ? 'Save changes' : 'Add entry'; echo '
'; echo '
'; echo '
Cancel
'; } else { if (mu()) { $vibe_items = coolquery("SELECT * FROM vibe_list ORDER BY id DESC"); } else { $vibe_items = coolquery("SELECT * FROM vibe_list WHERE thestatus=1 ORDER BY id DESC"); } if (!is_array($vibe_items)) { $vibe_items = array(); } if ($vibe_items) { // usort($vibe_items, function ($a, $b) use ($cvv_overall_rating_words) { // $va = cvv_vibe_rating_to_ten($a['overall_rating'] ?? '', $cvv_overall_rating_words); // $vb = cvv_vibe_rating_to_ten($b['overall_rating'] ?? '', $cvv_overall_rating_words); // if ($vb != $va) { // return $vb <=> $va; // } // return strcasecmp($a['name'] ?? '', $b['name'] ?? ''); // }); usort($vibe_items, function ($a, $b) { return strcasecmp($a['name'] ?? '', $b['name'] ?? ''); }); } $vibes_web = cvv_media_web_base(); $vibes_thumb_web = cvv_media_thumb_web_base(); if (mu()) { cvv_guestbook_ensure_tables(); $gb_entries = coolquery("SELECT * FROM `cvv_guestbook` ORDER BY created_at DESC, id DESC"); } else { $gb_entries = coolquery("SELECT * FROM `cvv_guestbook` WHERE thestatus=1 ORDER BY created_at DESC, id DESC"); } if (!is_array($gb_entries)) { $gb_entries = array(); } $gb_upload_web = cvv_media_web_base(); $gb_thumb_web = cvv_media_thumb_web_base(); $cvv_tab_q = strtolower(trim((string) ($_GET['tab'] ?? $_GET['section'] ?? ''))); $cvv_path_tab = cvv_path_tab_from_request(); $cvv_initial_tab = 'vibes'; if ($cvv_tab_q === 'info') { $cvv_initial_tab = 'info'; } elseif ($cvv_tab_q === 'guestbook') { $cvv_initial_tab = 'guestbook'; } elseif (mu() && $gb_edit_id_get > 0) { $cvv_initial_tab = 'guestbook'; } elseif ($cvv_tab_q === 'vibes' || $cvv_tab_q === 'vibez') { $cvv_initial_tab = 'vibes'; } elseif ($cvv_tab_q === '') { if ($cvv_path_tab === 'info') { $cvv_initial_tab = 'info'; } elseif ($cvv_path_tab === 'guestbook') { $cvv_initial_tab = 'guestbook'; } elseif ($cvv_path_tab === 'vibes') { $cvv_initial_tab = 'vibes'; } } $cvv_nav_on = 'background:#d8f4f4; border-color:#55bbbb;'; $cvv_nav_off = 'background:#f0f0f0; border-color:#ccc;'; if (!empty($_GET['posted'])) { echo '
Thanks — your guestbook memory was posted. It will be reviewed and should appear shortly.
'; } if (!empty($_GET['updated'])) { echo '
Guestbook entry updated.
'; } echo '
'; echo '
'; echo ''; echo ''; echo ''; echo '
'; echo '
'; echo '
'; $cvv_tag_filter_opts = array(); foreach ($vibe_list_bool_fields as $bf) { $cvv_tag_filter_opts[$bf] = $labels[$bf]; } $cvv_tag_filter_opts['crab_legs'] = $labels['crab_legs']; uasort($cvv_tag_filter_opts, function ($a, $b) { return strcasecmp($a, $b); }); if ($vibe_items) { echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo ''; echo '
'; echo '
'; echo '
'; $add_href = cvv_url_for_tab('vibes').'?addnew=1'; echo '
This is a list of places we\'ve been and recommend for your stay.
'; if (mu()) { echo '
Add new entry
'; } echo '
'; foreach ($vibe_items as $item) { $tag_slugs = array(); foreach ($vibe_list_bool_fields as $bf) { if (isset($item[$bf]) && (int) $item[$bf] !== 0) { $tag_slugs[] = $bf; } } if (vibe_list_crab_val($item)) { $tag_slugs[] = 'crab_legs'; } $data_tags = implode(' ', $tag_slugs); $ds_overall = cvv_vibe_rating_to_ten($item['overall_rating'] ?? '', $cvv_overall_rating_words); $ds_vibe = cvv_vibe_rating_to_ten($item['vibe_rating'] ?? '', $cvv_vibe_rating_words); $ds_name = htmlspecialchars(strtolower($item['name'] ?? ''), ENT_QUOTES, 'UTF-8'); $ds_price = 0; if (!empty($item['cost_level']) && is_numeric($item['cost_level'])) { $ds_price = max(0, min(5, (int) $item['cost_level'])); } $ds_distance = -1; if (isset($item['distance']) && is_numeric($item['distance']) && (float) $item['distance'] > 0) { $ds_distance = (float) $item['distance']; } $cvv_or_key = cvv_vibe_rating_num_from_raw((string) ($item['overall_rating'] ?? ''), $cvv_overall_rating_words); $data_fav = ($cvv_or_key === 5) ? '1' : '0'; $data_ts = cvv_vibe_list_row_is_approved($item) ? '1' : '0'; echo '
'; if (mu() && !cvv_vibe_list_row_is_approved($item)) { echo '
Pending
'; } $vibe_defer_extra_id = ''; $vibe_defer_extras = null; $vpn = cvv_vibe_row_photos($item); $vf = $vpn['files']; $vmain = $vpn['main']; if (count($vf) === 1) { $img_fn = $vf[0]; if (preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $img_fn)) { $full_src = $vibes_web.'/'.$img_fn; $thumb_src = cvv_media_thumb_url($img_fn, $vibes_web, $vibes_thumb_web); echo ''; } } elseif (count($vf) > 1) { if ($vmain === '' || !in_array($vmain, $vf, true)) { $vmain = $vf[0]; } $vextras = array_values(array_filter($vf, function ($f) use ($vmain) { return $f !== $vmain && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $f); })); $vid = intval($item['id'] ?? 0); $vex_id = 'vibe-extra-'.$vid; if (preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $vmain)) { $full_src = $vibes_web.'/'.$vmain; $thumb_src = cvv_media_thumb_url($vmain, $vibes_web, $vibes_thumb_web); echo '
'; echo '
'; if ($vextras) { echo ''; $vibe_defer_extra_id = $vex_id; $vibe_defer_extras = $vextras; } echo '
'; } elseif ($vextras) { echo ''; $vibe_defer_extra_id = $vex_id; $vibe_defer_extras = $vextras; } } echo '
'.$item['name'].'
'; // echo cvv_vibe_stars_html($item['overall_rating'] ?? '', $cvv_overall_rating_words); echo '
'; if ($cvv_or_key === 5) { echo '
Favorite Vibe!
'; } $cvv_item_cat_raw = trim((string) ($item['category'] ?? '')); $cvv_item_cat = cvv_vibe_category_normalize($cvv_item_cat_raw); if ($cvv_item_cat === '' && $cvv_item_cat_raw !== '') { $cvv_item_cat = $cvv_item_cat_raw; } echo '
Category: '.($cvv_item_cat === '' ? 'n/a' : htmlspecialchars($cvv_item_cat, ENT_QUOTES, 'UTF-8')).'
'; if (isset($item['distance']) && is_numeric($item['distance']) && (float) $item['distance'] > 0) { $cvv_card_d = (float) $item['distance']; $cvv_card_d_txt = (floor($cvv_card_d) == $cvv_card_d) ? (string) (int) $cvv_card_d : rtrim(rtrim(sprintf('%F', $cvv_card_d), '0'), '.'); echo '
Distance: '.htmlspecialchars($cvv_card_d_txt, ENT_QUOTES, 'UTF-8').' mi'; if ($cvv_card_d <= 0.25) { echo ' · steps away!'; } elseif ($cvv_card_d <= 0.5) { echo ' · walking distance'; } echo '
'; } if (!empty($item['vibe_rating'])) { $vw = cvv_vibe_rating_word_label($cvv_vibe_rating_words, $item['vibe_rating']); $vibe_line = ($vw !== '' ? $vw : (string) $item['vibe_rating']); echo '
Vibe: '.htmlspecialchars($vibe_line, ENT_QUOTES, 'UTF-8').'
'; } if (!empty($item['address'])) { $cvv_map_q = (string) $item['address']; $cvv_venue = trim((string) ($item['name'] ?? '')); if ($cvv_venue !== '') { $cvv_map_q = $cvv_venue.', '.$cvv_map_q; } echo ''; } if (!empty($item['website_url'])) { $wh = vibe_list_website_href($item['website_url']); if ($wh !== '') { echo ''; } } if (!empty($item['summary'])) { echo '
'.nl2br($item['summary']).'
'; } if (!empty($item['review'])) { echo '
'.nl2br($item['review']).'
'; } $tags_out = array(); foreach ($vibe_list_bool_fields as $bf) { if (isset($item[$bf]) && (int) $item[$bf] !== 0) { $tags_out[] = $labels[$bf]; } } if (vibe_list_crab_val($item)) { $tags_out[] = $labels['crab_legs']; } if ($tags_out) { echo '
Tags: '.implode(', ', $tags_out).'
'; } $details = array(); if (!empty($item['cost_level'])) { $details[] = 'Cost: '.str_repeat('$', (int) $item['cost_level']); } // Vibe label shown on its own line above (vibe_rating_words). // if (!empty($item['vibe_rating'])) { // $vw = cvv_vibe_rating_word_label($cvv_vibe_rating_words, $item['vibe_rating']); // $details[] = 'Vibe: '.($vw !== '' ? $vw : $item['vibe_rating']); // } if ($details) { echo '
'.implode(' | ', $details).'
'; } if ($vibe_defer_extra_id !== '' && $vibe_defer_extras) { echo ''; } if (mu()) { $edit_q = '?addnew=1&vibe_id='.intval($item['id']).'&return='.rawurlencode($return); $del_q = '?delete='.intval($item['id']).'&return='.rawurlencode($return); echo 'Edit   '; echo 'Delete'; } echo '
'; } echo '
'; echo ''; echo '
'; echo '
'; } else { echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo ''; echo '
'; echo '
'; echo '
'; echo '
No items found
'; $add_href = cvv_url_for_tab('vibes').'?addnew=1'; echo '
Add new entry
'; echo '
'; echo '
'; } echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo '
'; echo ''; echo '
'; echo '
'; echo '
'; echo '
WiFi: chillvibesvilla
Password: peaceandlove
'; echo 'Wi-Fi connection QR code'; echo '
'; echo '
'; echo '

'."You're officially on vacation now—time to slow down, relax, and vibe out. For a smooth and enjoyable experience for everyone, please take a moment to review the simple guidelines below.".'

'; echo '
'; echo '
'; echo '
Check-out instructions
'; echo '

Check-out time: '.'10:00 AM'.'

'; echo '

Before leaving:

'; echo '
    '; foreach (array( 'Empty refrigerator and freezer', 'Remove all trash and place in outside dumpster', 'Do not leave dirty dishes in the sink (you may run the dishwasher before leaving)', 'Return any moved furniture to its original position', ) as $info_line) { echo '
  • '.$info_line.'
  • '; } echo '
'; echo '
'; echo '
Keys / openers return
'; echo '

'.'BHHS Towne Realty'.'
'; echo '600 22nd Street, Suite 101'.'
'; echo 'Office opens at 9:30 AM'.'

'; echo '

'.'If checking out prior to 9:30 AM on Saturday, call '.'757-428-0201'.' for instructions'.'

'; echo '
'; echo '
'; echo '
House guidelines
'; echo '
    '; foreach (array( 'Keep noise at a reasonable level, especially at night (no sound audible inside neighboring homes)', 'No smoking or vaping anywhere indoors or from the balcony', 'Occupancy limit: No more than 3 people per bedroom after 11:00 PM', 'Events or parties are not permitted', 'Excessive mess, evidence of pets, or smoke odors will result in loss of security deposit', 'No fireworks or open fires', 'Don\'t overload the washing machine; Doing so can cause it to stop mid-cycle', 'Please treat our home as if it was your own', 'Great attitudes and fun times are mandatory', ) as $info_line) { echo '
  • '.$info_line.'
  • '; } echo '
'; echo '
'; echo '
Local contacts
'; echo '
    '; echo '
  • Non-emergency police: 757-385-5000
  • '; echo '
  • Emergency: 911
  • '; echo '
'; echo '
'; echo '
'; echo '
Address
'; echo '
Chill Vibes Villa
2113 Atlantic Ave #302, Virginia Beach, VA 23451
'; echo '
Booking
'; echo '
Berkshire Hathaway Home Services
'; echo '
'; echo '
'; echo '
Gallery
'; $cvv_condo_gallery = cvv_condo_gallery_list(); if (!$cvv_condo_gallery) { echo '

Add matching pairs of images to images/condo and thumbnails to images/condo/thumbs (same filename) to show the gallery.

'; } else { $cvv_cg_n = count($cvv_condo_gallery); echo ''; } echo '
'; // Vacation playlists — own bordered card above the cocktail card (do not nest inside drink panel markup). echo '
'; echo '
Official playlists (Spotify)
'; echo '

These are collated as the official soundtracks of the Chill Vibes Villa, featuring groovy ecclectic vibes from obscure artists you may not have heard of!

'; echo '
'; echo '
'; echo '

Chillvibesvilla daytime list: bright-but-easy beach-house energy for coffee on the balcony, sand-between-the-toes afternoons, or cruising with the sunroof open.

'; echo 'Open Daytime Playlist'; echo '
'; echo '
'; echo '

Chillvibesvilla nighttime list: smooth beats, mellow grooves, and deep nighttime tracks to go with warm, beachy nights.

'; echo 'Open Nighttime Playlist'; echo '
'; echo '
'; echo '
'; echo '
'; echo '
Official Cocktail: The Vibe Chiller
'; echo '
    '; echo '
  • 2 shots Smirnoff Green Apple
  • '; echo '
  • 2 shots lemonade
  • '; echo '
  • 2 shots Sprite
  • '; echo '
'; echo '

Pour over ice. Garnish with a lemon.

'; echo '
'; echo '
'; echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo '
'; echo ''; echo '
'; $gb_form_open_php = ($guestbook_err !== '' || $gb_edit_row); $gb_f_name = ''; $gb_f_when = ''; $gb_f_story = ''; if ($guestbook_err !== '') { $gb_f_name = (string) ($_POST['gb_name'] ?? ''); $gb_f_when = (string) ($_POST['gb_when'] ?? ''); $gb_f_story = (string) ($_POST['gb_story'] ?? ''); } elseif ($gb_edit_row) { $gb_f_name = (string) ($gb_edit_row['guest_name'] ?? ''); $gb_f_when = (string) ($gb_edit_row['visit_when'] ?? ''); $gb_f_story = (string) ($gb_edit_row['story'] ?? ''); } $gb_approved_checked = false; if (mu()) { if ($guestbook_err !== '') { $gb_approved_checked = !empty($_POST['gb_approved']); } elseif ($gb_edit_row) { $gb_approved_checked = cvv_guestbook_row_is_approved($gb_edit_row); } } echo '
'; echo '
'; echo '

Share when you visited and what you did. Add up to 5 photos if you like (JPEG/PNG/GIF/WebP, 4MB each). The first photo you upload is shown on the listing; you can pick a different main image when editing.

'; echo '

Entries may be reviewed before they appear. A hidden spam field helps block simple bots.

'; if ($guestbook_err !== '') { echo '
'.$guestbook_err.'
'; } echo '
'.($gb_edit_row ? 'Edit your visit' : 'Add your visit').'
'; echo '

Cancel

'; echo '
'; echo ''; echo '
'; echo ''; echo ''; echo ''; $gb_form_prev_files = array(); if ($gb_edit_row) { $gb_prev_norm = cvv_photos_normalize($gb_edit_row['photos_json'] ?? '[]'); $gb_form_prev_files = $gb_prev_norm['files']; $gb_form_main = $gb_prev_norm['main']; if ($gb_form_prev_files) { echo '
Current photos — pick one as the main image for the listing
'; echo '
'; foreach ($gb_form_prev_files as $gpfn) { if (!is_string($gpfn) || !preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $gpfn)) { continue; } $full_src = $gb_upload_web.'/'.$gpfn; $thumb_src = cvv_media_thumb_url($gpfn, $gb_upload_web, $gb_thumb_web); $main_chk = ($gpfn === $gb_form_main) ? ' checked' : ''; echo '
'; echo ''; echo ''; echo ''; echo '
'; } echo '
'; echo '
'; } } echo ''; if (mu()) { echo ''; } $gb_submit_lbl = $gb_edit_row ? 'Save changes' : 'Post to guestbook'; echo '
'; echo '
'; echo '
'; echo '
'; echo '
'; echo '
Please tell us your experience at Chill Vibes Villa.
'; echo ''; echo '
'; if ($gb_entries) { foreach ($gb_entries as $gbe) { echo '
'; echo '
'.($gbe['guest_name'] ?? '').'
'; if (mu() && !cvv_guestbook_row_is_approved($gbe)) { echo '
Pending
'; } $gb_written_ts = isset($gbe['thedate']) ? intval($gbe['thedate']) : 0; if ($gb_written_ts <= 0) { $gb_written_ts = strtotime($gbe['created_at'] ?? 'now'); if ($gb_written_ts === false) { $gb_written_ts = time(); } } $gb_written_rel = time_since($gb_written_ts, '', 1); $gb_vw_esc = htmlspecialchars((string) ($gbe['visit_when'] ?? ''), ENT_QUOTES, 'UTF-8'); echo '
'; echo '
Stayed '.$gb_vw_esc.'
'; echo '
Written '.htmlspecialchars($gb_written_rel, ENT_QUOTES, 'UTF-8').'
'; echo '
'; $gb_story = (string) ($gbe['story'] ?? ''); $gb_slen = function_exists('mb_strlen') ? mb_strlen($gb_story, 'UTF-8') : strlen($gb_story); if ($gb_slen > 500) { $gb_prev = function_exists('mb_substr') ? mb_substr($gb_story, 0, 500, 'UTF-8') : substr($gb_story, 0, 500); echo '
'; echo '
'.nl2br(htmlspecialchars($gb_prev, ENT_QUOTES, 'UTF-8')).'
'; echo ''; echo '
'; echo '
'; } else { echo '
'.nl2br(htmlspecialchars($gb_story, ENT_QUOTES, 'UTF-8')).'
'; } $gpn = cvv_photos_normalize($gbe['photos_json'] ?? '[]'); $gfiles = $gpn['files']; $gmain_fn = $gpn['main']; if (count($gfiles) === 1) { $pfn = $gfiles[0]; if (preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $pfn)) { $full_src = $gb_upload_web.'/'.$pfn; $thumb_src = cvv_media_thumb_url($pfn, $gb_upload_web, $gb_thumb_web); echo '
'; } } elseif (count($gfiles) > 1) { if ($gmain_fn === '' || !in_array($gmain_fn, $gfiles, true)) { $gmain_fn = $gfiles[0]; } $gb_ordered = array(); if (preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $gmain_fn)) { $gb_ordered[] = $gmain_fn; foreach ($gfiles as $pfn) { if ($pfn !== $gmain_fn && preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $pfn)) { $gb_ordered[] = $pfn; } } } else { foreach ($gfiles as $pfn) { if (preg_match('/^[a-f0-9]{24}\\.[a-z]+$/i', $pfn)) { $gb_ordered[] = $pfn; } } } if ($gb_ordered) { echo '
'; foreach ($gb_ordered as $pfn) { $full_src = $gb_upload_web.'/'.$pfn; $thumb_src = cvv_media_thumb_url($pfn, $gb_upload_web, $gb_thumb_web); echo ''; } echo '
'; } } if (mu()) { $gb_edit_h = cvv_url_for_tab('guestbook').'?gbedit='.intval($gbe['id'] ?? 0); $gb_del_h = cvv_url_for_tab('guestbook').'?gbdel='.intval($gbe['id'] ?? 0); echo ''; } echo '
'; } } else { echo '

No entries yet. Be the first to share a memory.

'; } echo '
'; echo '
'; echo '
'; ?>'; echo ''; echo ''; echo ''; echo '
Photo
'; echo ''; echo '
'; echo '

Please enjoy your stay, make memories, relax, and let the beach do its thang.

'; echo '

'.'We\'ll see you again soon.'.'

'; echo '
- Chill Vibes Villa Owners
'; echo '
'; echo ''; echo '
'; echo '
Wifi:
'; echo '

Scan with phone camera to connect

'; echo 'Wi-Fi connection QR code'; echo '
'; echo '
Network: chillvibesvilla    Password: peaceandlove
'; } ?>'; ?>