false, 'error' => 'Invalid URL.', 'body' => '', 'url' => '']; if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, CURLOPT_TIMEOUT => 25, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_USERAGENT => facebookEventImporterUserAgent(), CURLOPT_HTTPHEADER => ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'], ]); $body = curl_exec($ch); $errno = curl_errno($ch); $error = curl_error($ch); $status = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $finalUrl = (string)curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); if (PHP_VERSION_ID < 80000) curl_close($ch); if ($errno) return ['ok' => false, 'error' => $error ?: 'Request failed.', 'body' => '', 'url' => $url]; if ($status < 200 || $status >= 400) return ['ok' => false, 'error' => 'HTTP '.$status.' from Facebook.', 'body' => (string)$body, 'url' => $finalUrl ?: $url]; return ['ok' => true, 'error' => '', 'body' => (string)$body, 'url' => $finalUrl ?: $url]; } $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'timeout' => 25, 'ignore_errors' => true, 'header' => "User-Agent: ".facebookEventImporterUserAgent()."\r\nAccept: text/html\r\n", ], ]); $body = @file_get_contents($url, false, $context); $statusLine = $http_response_header[0] ?? ''; if ($body === false || ($statusLine && !preg_match('/\s[23]\d\d\s/', $statusLine))) { return ['ok' => false, 'error' => $statusLine ?: 'Request failed.', 'body' => (string)$body, 'url' => $url]; } return ['ok' => true, 'error' => '', 'body' => (string)$body, 'url' => $url]; } function facebookEventDom(string $html): ?DOMDocument { if (!class_exists('DOMDocument')) return null; $dom = new DOMDocument(); $old = libxml_use_internal_errors(true); $loaded = $dom->loadHTML(''.$html); libxml_clear_errors(); libxml_use_internal_errors($old); return $loaded ? $dom : null; } function facebookEventMeta(string $html): array { $dom = facebookEventDom($html); if (!$dom) return []; $out = []; foreach ($dom->getElementsByTagName('meta') as $meta) { $key = $meta->getAttribute('property') ?: $meta->getAttribute('name'); $content = $meta->getAttribute('content'); if ($key !== '' && $content !== '') $out[strtolower($key)] = $content; } foreach ($dom->getElementsByTagName('link') as $link) { if (strtolower($link->getAttribute('rel')) === 'canonical' && $link->getAttribute('href') !== '') { $out['canonical'] = $link->getAttribute('href'); } } return $out; } function facebookEventFindJsonLdEvent(mixed $node): ?array { if (!is_array($node)) return null; $type = $node['@type'] ?? ''; $types = is_array($type) ? $type : [$type]; foreach ($types as $candidateType) { if (strtolower((string)$candidateType) === 'event') return $node; } foreach ($node as $child) { $found = facebookEventFindJsonLdEvent($child); if ($found) return $found; } return null; } function facebookEventJsonLd(string $html): ?array { $dom = facebookEventDom($html); if (!$dom) return null; foreach ($dom->getElementsByTagName('script') as $script) { $type = strtolower($script->getAttribute('type')); if ($type !== 'application/ld+json') continue; $json = trim($script->textContent ?? ''); if ($json === '') continue; $decoded = json_decode($json, true); $found = facebookEventFindJsonLdEvent($decoded); if ($found) return $found; } return null; } function facebookEventImageFromValue(mixed $value): string { if (is_string($value)) return $value; if (is_array($value)) { if (isset($value['url'])) return facebookEventImageFromValue($value['url']); foreach ($value as $item) { $image = facebookEventImageFromValue($item); if ($image !== '') return $image; } } return ''; } function facebookEventAddressFromLocation(mixed $location): array { $venue = ''; $address = ''; if (is_string($location)) return [$location, '']; if (!is_array($location)) return ['', '']; $venue = facebookEventText($location['name'] ?? ''); $addr = $location['address'] ?? ''; if (is_string($addr)) { $address = facebookEventText($addr); } elseif (is_array($addr)) { $parts = []; foreach (['streetAddress', 'addressLocality', 'addressRegion', 'postalCode'] as $key) { if (!empty($addr[$key])) $parts[] = facebookEventText($addr[$key]); } $address = implode(', ', array_filter($parts)); } return [$venue, $address]; } function facebookEventSplitDateTime(string $value): array { $value = facebookEventText(str_replace([' at ', ' AT '], ' ', $value)); if ($value === '') return ['', '']; $hasTime = (bool)preg_match('/(?:T|\b\d{1,2}:\d{2}\b|\b\d{1,2}\s*(?:am|pm)\b)/i', $value); try { $dt = new DateTimeImmutable($value); } catch (Throwable) { return ['', '']; } return [$dt->format('Y-m-d'), $hasTime ? $dt->format('g:i A') : '']; } function facebookEventDateFromText(string $text): array { $months = 'Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?'; if (preg_match('/\b(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)?(?:day)?[,]?\s*(?:'.$months.')\s+\d{1,2}(?:,\s*\d{4})?(?:\s+(?:at|@)\s+\d{1,2}(?::\d{2})?\s*(?:am|pm)?)?/i', $text, $match)) { return facebookEventSplitDateTime($match[0]); } if (preg_match('/\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{1,2}:\d{2}(?::\d{2})?)?/i', $text, $match)) { return facebookEventSplitDateTime($match[0]); } return ['', '']; } function facebookEventCleanTitle(string $title): string { $title = facebookEventText($title); $title = preg_replace('/\s*\|\s*Facebook\s*$/i', '', $title); $title = preg_replace('/\s*-\s*Facebook\s*$/i', '', $title); return trim($title); } function facebookEventWebsiteForStorage(string $url): string { $url = trim($url); if ($url === '') return ''; $url = preg_replace('~^https?://~i', '', $url); return preg_replace('~/+$~', '', $url); } function facebookEventExtractEventUrls(string $html, string $sourceUrl = 'https://www.facebook.com'): array { $links = []; $dom = facebookEventDom($html); if ($dom) { foreach ($dom->getElementsByTagName('a') as $a) { $href = $a->getAttribute('href'); $url = facebookEventNormalizeEventUrl($href, $sourceUrl); if ($url !== '') $links[$url] = $url; } } $decoded = html_entity_decode($html, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $decoded = str_replace(['\\/', '\\u002F', '\\u003A', '\\u0026'], ['/', '/', ':', '&'], $decoded); if (preg_match_all('~https?://(?:www\.)?facebook\.com/(?:events|share)/[^\s"\'<>\\\\]+~i', $decoded, $matches)) { foreach ($matches[0] as $raw) { $url = facebookEventNormalizeEventUrl($raw, $sourceUrl); if ($url !== '') $links[$url] = $url; } } if (preg_match_all('~href=["\']([^"\']*(?:/events/|/share/)[^"\']*)["\']~i', $decoded, $matches)) { foreach ($matches[1] as $raw) { $url = facebookEventNormalizeEventUrl($raw, $sourceUrl); if ($url !== '') $links[$url] = $url; } } return array_values($links); } function facebookEventParseHtml(string $html, string $sourceUrl = '', string $area = ''): array { $jsonLd = facebookEventJsonLd($html); $meta = facebookEventMeta($html); $sourceUrl = facebookEventNormalizeEventUrl($sourceUrl ?: ($meta['og:url'] ?? $meta['canonical'] ?? '')) ?: facebookEventNormalizeUrl($sourceUrl ?: ($meta['og:url'] ?? $meta['canonical'] ?? '')); $candidate = [ 'provider' => 'facebook', 'source_area' => $area, 'source_url' => $sourceUrl, 'external_id' => facebookEventExternalId($sourceUrl), 'title' => '', 'description' => '', 'venue' => '', 'address' => '', 'event_date' => '', 'event_time' => '', 'end_date' => null, 'end_time' => '', 'cost' => 'Free', 'website' => facebookEventWebsiteForStorage($sourceUrl), 'image_url' => '', 'image_path' => '', 'raw_json' => ['json_ld' => $jsonLd, 'meta' => $meta], ]; if ($jsonLd) { $candidate['title'] = facebookEventCleanTitle((string)($jsonLd['name'] ?? '')); $candidate['description'] = facebookEventText($jsonLd['description'] ?? ''); [$candidate['event_date'], $candidate['event_time']] = facebookEventSplitDateTime((string)($jsonLd['startDate'] ?? '')); [$endDate, $endTime] = facebookEventSplitDateTime((string)($jsonLd['endDate'] ?? '')); $candidate['end_date'] = $endDate ?: null; $candidate['end_time'] = $endTime; [$candidate['venue'], $candidate['address']] = facebookEventAddressFromLocation($jsonLd['location'] ?? null); $candidate['image_url'] = facebookEventNormalizeUrl(facebookEventImageFromValue($jsonLd['image'] ?? ''), $sourceUrl ?: 'https://www.facebook.com'); if (!empty($jsonLd['url'])) { $candidate['source_url'] = facebookEventNormalizeEventUrl((string)$jsonLd['url'], $sourceUrl ?: 'https://www.facebook.com') ?: $candidate['source_url']; $candidate['website'] = facebookEventWebsiteForStorage($candidate['source_url']); $candidate['external_id'] = facebookEventExternalId($candidate['source_url']); } if (!empty($jsonLd['offers']['price'])) $candidate['cost'] = facebookEventText($jsonLd['offers']['price']); } if ($candidate['title'] === '') $candidate['title'] = facebookEventCleanTitle($meta['og:title'] ?? $meta['twitter:title'] ?? ''); if ($candidate['description'] === '') $candidate['description'] = facebookEventText($meta['og:description'] ?? $meta['description'] ?? $meta['twitter:description'] ?? ''); if ($candidate['image_url'] === '') $candidate['image_url'] = facebookEventNormalizeUrl($meta['og:image'] ?? $meta['twitter:image'] ?? '', $sourceUrl ?: 'https://www.facebook.com'); if ($candidate['event_date'] === '') { [$candidate['event_date'], $candidate['event_time']] = facebookEventDateFromText($candidate['title'].' '.$candidate['description']); } if ($candidate['source_url'] === '' && !empty($meta['og:url'])) { $candidate['source_url'] = facebookEventNormalizeUrl($meta['og:url']); $candidate['website'] = facebookEventWebsiteForStorage($candidate['source_url']); $candidate['external_id'] = facebookEventExternalId($candidate['source_url']); } return $candidate; } function facebookGraphEventUrl(string $eventId): string { $eventId = trim($eventId); return $eventId !== '' ? 'https://www.facebook.com/events/'.rawurlencode($eventId) : ''; } function facebookGraphPlaceToVenueAddress(mixed $place): array { if (!is_array($place)) return ['', '']; $venue = facebookEventText($place['name'] ?? ''); $location = $place['location'] ?? []; if (!is_array($location)) return [$venue, '']; $parts = []; foreach (['street', 'city', 'state', 'zip', 'country'] as $key) { if (!empty($location[$key])) $parts[] = facebookEventText($location[$key]); } return [$venue, implode(', ', array_filter($parts))]; } function facebookGraphEventToCandidate(array $event, string $area = ''): array { $eventId = trim((string)($event['id'] ?? '')); $sourceUrl = facebookGraphEventUrl($eventId); [$eventDate, $eventTime] = facebookEventSplitDateTime((string)($event['start_time'] ?? '')); [$endDate, $endTime] = facebookEventSplitDateTime((string)($event['end_time'] ?? '')); [$venue, $address] = facebookGraphPlaceToVenueAddress($event['place'] ?? null); $ticketUrl = facebookEventNormalizeUrl((string)($event['ticket_uri'] ?? '')); $website = $ticketUrl !== '' ? $ticketUrl : $sourceUrl; $cover = $event['cover'] ?? []; $imageUrl = is_array($cover) ? facebookEventNormalizeUrl((string)($cover['source'] ?? '')) : ''; return [ 'provider' => 'facebook', 'source_area' => $area, 'source_url' => $sourceUrl, 'external_id' => $eventId, 'title' => facebookEventCleanTitle((string)($event['name'] ?? '')), 'description' => facebookEventText($event['description'] ?? ''), 'venue' => $venue, 'address' => $address, 'event_date' => $eventDate, 'event_time' => $eventTime, 'end_date' => $endDate ?: null, 'end_time' => $endTime, 'cost' => 'Free', 'website' => facebookEventWebsiteForStorage($website), 'image_url' => $imageUrl, 'image_path' => '', 'raw_json' => ['graph_event' => $event], ]; } function facebookEventDownloadImage(string $imageUrl): array { $imageUrl = facebookEventNormalizeUrl($imageUrl); if ($imageUrl === '') return ['ok' => true, 'path' => '', 'error' => '']; if (!facebookEventAllowedImageHost($imageUrl)) { return ['ok' => false, 'path' => '', 'error' => 'Image host is not a Facebook image host.']; } $maxBytes = mbToBytes(setting('event_upload_max_mb', '5'), 5); $tmp = tempnam(sys_get_temp_dir(), 'fb-event-img-'); if (!$tmp) return ['ok' => false, 'path' => '', 'error' => 'Could not create a temporary image file.']; $mime = ''; $size = 0; if (function_exists('curl_init')) { $fh = fopen($tmp, 'wb'); if (!$fh) return ['ok' => false, 'path' => '', 'error' => 'Could not write a temporary image file.']; $ch = curl_init($imageUrl); curl_setopt_array($ch, [ CURLOPT_FILE => $fh, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, CURLOPT_TIMEOUT => 25, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_USERAGENT => facebookEventImporterUserAgent(), CURLOPT_WRITEFUNCTION => function ($ch, string $chunk) use ($fh, $maxBytes, &$size): int { $size += strlen($chunk); if ($size > $maxBytes) return 0; $written = fwrite($fh, $chunk); return $written === false ? 0 : $written; }, ]); $ok = curl_exec($ch); $errno = curl_errno($ch); $error = curl_error($ch); $status = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $mime = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE); if (PHP_VERSION_ID < 80000) curl_close($ch); fclose($fh); if (!$ok || $errno || $status < 200 || $status >= 400) { @unlink($tmp); $message = $size > $maxBytes ? 'Image exceeds the configured size limit.' : ($error ?: 'Image download failed.'); return ['ok' => false, 'path' => '', 'error' => $message]; } } else { $context = stream_context_create(['http' => ['timeout' => 25, 'ignore_errors' => true, 'header' => 'User-Agent: '.facebookEventImporterUserAgent()."\r\n"]]); $body = @file_get_contents($imageUrl, false, $context); if ($body === false || strlen($body) > $maxBytes) { @unlink($tmp); return ['ok' => false, 'path' => '', 'error' => $body === false ? 'Image download failed.' : 'Image exceeds the configured size limit.']; } file_put_contents($tmp, $body); } if (!function_exists('getimagesize') || !@getimagesize($tmp)) { @unlink($tmp); return ['ok' => false, 'path' => '', 'error' => 'Downloaded file is not a valid image.']; } if ($mime === '' && function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); if ($finfo) { $mime = (string)finfo_file($finfo, $tmp); finfo_close($finfo); } } $ext = match (strtolower(strtok($mime, ';') ?: '')) { 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp', default => 'jpg', }; $stored = storeUploadedFile( ['name' => 'facebook-event.'.$ext, 'tmp_name' => $tmp, 'error' => UPLOAD_ERR_OK, 'size' => filesize($tmp) ?: 0], 'events', imageExts(), $maxBytes, [ 'image_only' => true, 'resize_over_bytes' => mbToBytes(setting('forum_image_resize_threshold_mb', '1'), 1), 'image_max_width' => (int)setting('forum_image_max_width', '1600'), 'image_quality' => (int)setting('forum_image_quality', '82'), ] ); if (!$stored['ok']) @unlink($tmp); return $stored['ok'] ? ['ok' => true, 'path' => (string)$stored['path'], 'error' => ''] : ['ok' => false, 'path' => '', 'error' => $stored['error'] ?? 'Image could not be saved.']; } function facebookEventCandidateIssues(array $candidate): array { $issues = []; if (trim((string)($candidate['title'] ?? '')) === '') $issues[] = 'Missing title'; $date = trim((string)($candidate['event_date'] ?? '')); $dt = DateTimeImmutable::createFromFormat('!Y-m-d', $date); if ($date === '' || !$dt || $dt->format('Y-m-d') !== $date) $issues[] = 'Missing valid start date'; return $issues; } function facebookEventDuplicateId(array $candidate): int { $title = trim((string)($candidate['title'] ?? '')); $date = trim((string)($candidate['event_date'] ?? '')); if ($title === '' || $date === '') return 0; return (int)qval("SELECT id FROM events WHERE lower(title)=lower(?) AND event_date=? LIMIT 1", [$title, $date]); } function facebookEventStageCandidate(array $candidate, bool $downloadImage = true): array { $provider = 'facebook'; $sourceUrl = facebookEventNormalizeEventUrl((string)($candidate['source_url'] ?? '')) ?: facebookEventNormalizeUrl((string)($candidate['source_url'] ?? '')); $candidate['source_url'] = $sourceUrl; $candidate['external_id'] = trim((string)($candidate['external_id'] ?? facebookEventExternalId($sourceUrl))); $candidate['website'] = facebookEventWebsiteForStorage((string)($candidate['website'] ?? $sourceUrl)); $candidate['cost'] = trim((string)($candidate['cost'] ?? '')) ?: 'Free'; $candidate['end_date'] = ($candidate['end_date'] ?? '') ?: null; $candidate['raw_json'] = $candidate['raw_json'] ?? []; if ($downloadImage && empty($candidate['image_path']) && !empty($candidate['image_url'])) { $image = facebookEventDownloadImage((string)$candidate['image_url']); if ($image['ok'] && !empty($image['path'])) { $candidate['image_path'] = $image['path']; } elseif (!$image['ok']) { $candidate['status_note'] = 'Image not downloaded: '.$image['error']; } } $existing = null; if ($sourceUrl !== '') $existing = qone("SELECT * FROM event_imports WHERE provider=? AND source_url=? LIMIT 1", [$provider, $sourceUrl]); if (!$existing && $candidate['external_id'] !== '') { $existing = qone("SELECT * FROM event_imports WHERE provider=? AND external_id=? LIMIT 1", [$provider, $candidate['external_id']]); } $fields = [ 'provider' => $provider, 'source_area' => facebookEventText($candidate['source_area'] ?? ''), 'source_url' => $sourceUrl, 'external_id' => $candidate['external_id'], 'title' => facebookEventCleanTitle((string)($candidate['title'] ?? '')), 'description' => facebookEventText($candidate['description'] ?? ''), 'venue' => facebookEventText($candidate['venue'] ?? ''), 'address' => facebookEventText($candidate['address'] ?? ''), 'event_date' => trim((string)($candidate['event_date'] ?? '')), 'event_time' => facebookEventText($candidate['event_time'] ?? ''), 'end_date' => $candidate['end_date'], 'end_time' => facebookEventText($candidate['end_time'] ?? ''), 'cost' => facebookEventText($candidate['cost']), 'website' => facebookEventWebsiteForStorage((string)$candidate['website']), 'image_url' => facebookEventNormalizeUrl((string)($candidate['image_url'] ?? ''), $sourceUrl ?: 'https://www.facebook.com'), 'image_path' => trim((string)($candidate['image_path'] ?? '')), 'raw_json' => json_encode($candidate['raw_json'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '{}', 'status_note' => facebookEventText($candidate['status_note'] ?? ''), ]; if ($existing) { if (($existing['status'] ?? '') === 'imported') { return ['ok' => true, 'id' => (int)$existing['id'], 'action' => 'already_imported']; } qrun( "UPDATE event_imports SET source_area=?,source_url=?,external_id=?,title=?,description=?,venue=?,address=?,event_date=?,event_time=?,end_date=?,end_time=?,cost=?,website=?,image_url=?,image_path=?,raw_json=?,status='staged',status_note=?,updated_at=datetime('now') WHERE id=?", [$fields['source_area'], $fields['source_url'], $fields['external_id'], $fields['title'], $fields['description'], $fields['venue'], $fields['address'], $fields['event_date'], $fields['event_time'], $fields['end_date'], $fields['end_time'], $fields['cost'], $fields['website'], $fields['image_url'], $fields['image_path'], $fields['raw_json'], $fields['status_note'], (int)$existing['id']] ); return ['ok' => true, 'id' => (int)$existing['id'], 'action' => 'updated']; } $id = qrun( "INSERT INTO event_imports(provider,source_area,source_url,external_id,title,description,venue,address,event_date,event_time,end_date,end_time,cost,website,image_url,image_path,raw_json,status,status_note)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'staged',?)", [$fields['provider'], $fields['source_area'], $fields['source_url'], $fields['external_id'], $fields['title'], $fields['description'], $fields['venue'], $fields['address'], $fields['event_date'], $fields['event_time'], $fields['end_date'], $fields['end_time'], $fields['cost'], $fields['website'], $fields['image_url'], $fields['image_path'], $fields['raw_json'], $fields['status_note']] ); return ['ok' => true, 'id' => $id, 'action' => 'created']; } function facebookEventSaveCandidateFields(int $id, array $row): void { qrun( "UPDATE event_imports SET title=?,description=?,venue=?,address=?,event_date=?,event_time=?,end_date=?,end_time=?,cost=?,website=?,image_path=?,status_note='',updated_at=datetime('now') WHERE id=? AND status!='imported'", [ facebookEventCleanTitle((string)($row['title'] ?? '')), facebookEventText($row['description'] ?? ''), facebookEventText($row['venue'] ?? ''), facebookEventText($row['address'] ?? ''), trim((string)($row['event_date'] ?? '')), facebookEventText($row['event_time'] ?? ''), trim((string)($row['end_date'] ?? '')) ?: null, facebookEventText($row['end_time'] ?? ''), facebookEventText($row['cost'] ?? 'Free') ?: 'Free', facebookEventWebsiteForStorage((string)($row['website'] ?? '')), trim((string)($row['image_path'] ?? '')), $id, ] ); } function facebookEventImportCandidate(int $id, int $userId, string $eventStatus = 'pending', bool $allowDuplicate = false): array { $candidate = qone("SELECT * FROM event_imports WHERE id=?", [$id]); if (!$candidate) return ['ok' => false, 'error' => 'Import candidate not found.']; if (($candidate['status'] ?? '') === 'imported') return ['ok' => false, 'error' => 'This candidate has already been imported.']; $issues = facebookEventCandidateIssues($candidate); if ($issues) return ['ok' => false, 'error' => implode('; ', $issues).'.']; $duplicateId = facebookEventDuplicateId($candidate); if ($duplicateId && !$allowDuplicate) return ['ok' => false, 'error' => 'Possible duplicate of event #'.$duplicateId.'.']; $eventStatus = in_array($eventStatus, ['pending', 'approved'], true) ? $eventStatus : 'pending'; $imagePath = (string)($candidate['image_path'] ?? ''); $note = ''; if ($imagePath === '' && !empty($candidate['image_url'])) { $image = facebookEventDownloadImage((string)$candidate['image_url']); if ($image['ok'] && !empty($image['path'])) { $imagePath = (string)$image['path']; } elseif (!$image['ok']) { $note = 'Imported without image: '.$image['error']; } } $eventId = qrun( "INSERT INTO events(title,description,venue,address,event_date,event_time,end_date,end_time,cost,website,contact_name,contact_email,contact_phone,image_path,submitted_by,status,is_featured)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)", [ $candidate['title'], $candidate['description'], $candidate['venue'], $candidate['address'], $candidate['event_date'], $candidate['event_time'], $candidate['end_date'] ?: null, $candidate['end_time'], $candidate['cost'] ?: 'Free', facebookEventWebsiteForStorage((string)($candidate['website'] ?: $candidate['source_url'])), '', '', '', $imagePath, $userId ?: null, $eventStatus, ] ); qrun("UPDATE event_imports SET status='imported',imported_event_id=?,image_path=?,status_note=?,updated_at=datetime('now') WHERE id=?", [$eventId, $imagePath, $note, $id]); return ['ok' => true, 'event_id' => $eventId, 'warning' => $note]; }