File: /home/awaldron/_phpassport/api.pennhillspassport.com/v1/_globals.php
<?php
//**********************************************************************************************************//
// TRIB NEWS HELPERS **************************************************************************************//
function getFirstCarouselImage($url)
{
// Decide carousel ID based on URL
$id = (strpos($url, 'tribhssn.triblive.com') !== false)
? 'postCarousel'
: 'storyCarousel';
$html = @file_get_contents($url);
if ($html === false) {
return null;
}
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
// First <img> inside the chosen carousel
$img = $xpath->query(
'//*[@id="'.$id.'"]//img'
)->item(0);
if ($img && $img->hasAttribute('src')) {
return trim($img->getAttribute('src'));
}
return null;
}
function getFirstCarouselThumb($url)
{
// Decide carousel ID based on URL
$id = (strpos($url, 'tribhssn.triblive.com') !== false)
? 'postCarousel'
: 'storyCarousel';
$html = @file_get_contents($url);
if ($html === false) {
return null;
}
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
// First thumb-wrap inside storyCarousel
$node = $xpath->query(
'//*[@id="'.$id.'"]//*[contains(@class,"thumb-wrap")]'
)->item(0);
if ($node && $node->hasAttribute('style')) {
if (preg_match("/url\\(['\"]?(.*?)['\"]?\\)/", $node->getAttribute('style'), $m)) {
return $m[1];
}
}
return null;
}
//**********************************************************************************************************//
// DATABASE FUNCTIONS **************************************************************************************//
function dbconnect()
{
$db = null;
// LOCALHOST
if ($_SERVER['HTTP_HOST'] == 'localhost:8888') {
$db = new mysqli('localhost', 'root', 'root', 'phcdc_members');
} // PRODUCTION
else if ($_SERVER['HTTP_HOST'] == 'api.pennhillspassport.com') {
$db = new mysqli('localhost', 'awaldron_phpassport_user', '35&LtmeL6C*5', 'awaldron_phpassport');
}
if ($db && $db->connect_errno) {
response(500, ['success' => false, 'errors' => ['Error connecting to the database.']]);
}
$db->set_charset("utf8mb4");
return $db;
}
//**********************************************************************************************************//
// CORS FUNCTIONS ******************************************************************************************//
function cors()
{
// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
// Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
// you want to allow, and if so:
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
// may also be using PUT, PATCH, HEAD etc
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
}
//**********************************************************************************************************//
// API FUNCTIONS *******************************************************************************************//
function validate_body($args, $rules)
{
$valid = true;
$errors = [];
if ($args !== false && $rules !== false) {
foreach ($rules as $param => $rule) {
// CHECK REQUIRED
if (strstr($rule, 'req')) {
if (!isset($args[$param])) {
$valid = false;
$errors[] = $param . ' is required.';
}
}
// CHECK EMAIL
if (strstr($rule, 'email')) {
if (!filter_var($args[$param], FILTER_VALIDATE_EMAIL)) {
$valid = false;
$errors[] = $param . ' is not a valid email address.';
}
}
}
}
if (!$valid) {
response(400, ['success' => false, 'errors' => $errors, 'payload' => $args]);
}
return true;
}
function validate_token($uid)
{
// IF uid==0 THEN THROW ERROR INVALID TOKEN
if ($uid !== false && $uid === 0) {
response(401, ['success' => false, 'errors' => ['Security token invalid.']]);
}
}
function validate_admin($is_admin)
{
// IF is_admin==0 THEN THROW ERROR NOT AUTHORIZED
if (!$is_admin) {
response(401, ['success' => false, 'errors' => ['Unauthorized. User must be an admin.']]);
}
}
function utf8ize($d)
{
if (is_array($d)) {
foreach ($d as $k => $v) {
$d[$k] = utf8ize($v);
}
} else if (is_string($d)) {
return utf8_encode($d);
}
return $d;
}
function response($responseCode = 200, $responseBody = [])
{
http_response_code($responseCode);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($responseBody);
exit;
}
function new_token($db, $uid)
{
$token = bin2hex(random_bytes(64));
$created = time();
$expires = time() + 60 * 60 * 12;
$ins = "INSERT INTO tokens
(user_id, token, created, expires) VALUES
(" . $uid . ",'" . $token . "'," . $created . "," . $expires . ")";
if ($result = $db->query($ins)) {
return [
'token' => $token,
'created' => $created,
'expires' => $expires
];
} else {
response(400, ['success' => false, 'errors' => ['Error creating token.']]);
}
}
function check_token($db, $token)
{
$sql = "SELECT token_id, user_id FROM tokens WHERE token='" . $token . "' AND expires > " . time();
$res = qRow($db, $sql);
if (!empty($res['user_id'])) {
// UPDATE TOKEN EXPIRATION
$upd = "UPDATE tokens SET expires=" . (time() + 60 * 60 * 12) . " WHERE token_id=" . $res['token_id'];
$db->query($upd);
return $res['user_id'];
} else {
return 0;
}
}
function randomString($n)
{
$characters = '01234567890123456789ABCDEFGHIJKLMNPQRSTUVWXYZABCDEFGHIJKLMNPQRSTUVWXYZ';
$randomString = '';
for ($i = 0; $i < $n; $i++) {
$index = rand(0, strlen($characters) - 1);
$randomString .= $characters[$index];
}
return $randomString;
}
function randomPassword($n)
{
$uppers = "ABCDEFGHIJKLMNPQRSTUVWXYZABCDEFGHIJKLMNPQRSTUVWXYZ";
$downers = "abcdefghijklmnpqrstuvwxyzabcdefghijklmnpqrstuvwxyz";
$numbers = "01234567890123456789";
$symbols = "!@#$%&*!@#$%&*";
$randomString = '';
// 1 Uppercase
for ($i = 0; $i < 1; $i++) {
$index = rand(0, strlen($uppers) - 1);
$randomString .= $uppers[$index];
}
// 3 Lowercase
for ($i = 0; $i < 3; $i++) {
$index = rand(0, strlen($downers) - 1);
$randomString .= $downers[$index];
}
// 4 Numbers
for ($i = 0; $i < 4; $i++) {
$index = rand(0, strlen($numbers) - 1);
$randomString .= $numbers[$index];
}
// 1 symbols
for ($i = 0; $i < 1; $i++) {
$index = rand(0, strlen($symbols) - 1);
$randomString .= $symbols[$index];
}
return $randomString;
}
//**********************************************************************************************************//
// DB HELPER FUNCTIONS *************************************************************************************//
function qVal($db, $sql)
{
$res = $db->query($sql);
if ($res->num_rows > 0) {
$row = $res->fetch_assoc();
foreach ($row as $k => $v) {
return $v;
}
} else {
return false;
}
}
function qRow($db, $sql)
{
$res = $db->query($sql);
if ($res->num_rows > 0) {
$row = $res->fetch_assoc();
return $row;
} else {
return false;
}
}
function qSet($db, $sql)
{
$out = [];
$res = $db->query($sql);
while ($row = $res->fetch_assoc()) {
$out[] = $row;
}
return $out;
}
function qCol($db, $sql)
{
$out = [];
$res = $db->query($sql);
while ($row = $res->fetch_assoc()) {
foreach ($row as $key => $val) {
$out[] = $val;
}
}
return $out;
}
function qsql($q)
{
if ($q->num_rows() == 0) return false;
else {
$r = $q->result_array();
$r = $r[0];
foreach ($r as $k => $v) return $v;
}
}
function qin($a)
{
$in = '(';
for ($i = 0; $i < count($a); $i++) {
if ($i > 0) $in .= ',';
$in .= "'" . $a[$i] . "'";
}
$in .= ')';
return $in;
}
function shut()
{
$error = error_get_last();
if ($error && ($error['type'] & E_FATAL)) {
handler($error['type'], $error['message'], $error['file'], $error['line']);
}
}
function handler($errno, $errstr, $errfile, $errline)
{
switch ($errno) {
case E_ERROR: // 1 //
$typestr = 'E_ERROR';
break;
case E_WARNING: // 2 //
$typestr = 'E_WARNING';
break;
case E_PARSE: // 4 //
$typestr = 'E_PARSE';
break;
case E_NOTICE: // 8 //
$typestr = 'E_NOTICE';
break;
case E_CORE_ERROR: // 16 //
$typestr = 'E_CORE_ERROR';
break;
case E_CORE_WARNING: // 32 //
$typestr = 'E_CORE_WARNING';
break;
case E_COMPILE_ERROR: // 64 //
$typestr = 'E_COMPILE_ERROR';
break;
case E_COMPILE_WARNING: // 128 //
$typestr = 'E_COMPILE_WARNING';
break;
case E_USER_ERROR: // 256 //
$typestr = 'E_USER_ERROR';
break;
case E_USER_WARNING: // 512 //
$typestr = 'E_USER_WARNING';
break;
case E_USER_NOTICE: // 1024 //
$typestr = 'E_USER_NOTICE';
break;
case E_STRICT: // 2048 //
$typestr = 'E_STRICT';
break;
case E_RECOVERABLE_ERROR: // 4096 //
$typestr = 'E_RECOVERABLE_ERROR';
break;
case E_DEPRECATED: // 8192 //
$typestr = 'E_DEPRECATED';
break;
case E_USER_DEPRECATED: // 16384 //
$typestr = 'E_USER_DEPRECATED';
break;
}
$message = '<b>' . $typestr . ': </b>' . $errstr . ' in <b>' . $errfile . '</b> on line <b>' . $errline . '</b><br/>';
if (($errno & E_FATAL) && ENV === 'production') {
header('Location: 500.html');
header('Status: 500 Internal Server Error');
}
if (!($errno & ERROR_REPORTING))
return;
if (DISPLAY_ERRORS) {
response(400, ['success' => false, 'error' => $message]);
//printf('%s', $message);
}
//Logging error on php file error log...
if (LOG_ERRORS)
error_log(strip_tags($message), 0);
}
function get_email_template($body, $m, $ecid)
{
$email = <<<END
<html>
<head>
<body>
<div style="width:100%; background-color:#F6F6F6; padding:20px 0">
<div style="background-color:#FFFFFF; max-width:600px;margin:0 auto;font-size:16px;line-height:1.6em">
<div style="background-color:#33612a; padding:20px; border-bottom:solid 4px #efa72d">
<a href="https://pennhillscdc.org" target="_blank" style="display:block;text-align:center;">
<img src="https://pennhillscdc.org/e/email-header.png" width="200" height="148" />
</a>
</div>
<div style="padding:20px;">
<p style="font-size:24px;color:#efa72d;font-weight:bold">[SUBJECT]</p>
<p>Hi [NAME],</p>
[BODY]
<p> <br/><b>Have a great day!</b>
<br/>Your friends at the Penn Hills CDC
</p>
</div>
<div style="background-color:#33612a; padding:20px; border-top:solid 4px #efa72d">
<a href="https://pennhillscdc.org" target="_blank" style="display:block;text-align:center;">
<img src="https://api.pennhillscdc.org/v1/mail/read/[MAILCODE]/[ECID]" width="180" height="44" />
</a>
<p style="color:#FFFFFF; margin-top:12px; font-size:13px; text-align:center; line-height: 1.3em">
<b style="color:#efa72d">Penn Hills Community Development Corporation</b>
<br/>PO Box 17730
<br/>Penn Hills, PA 15235
<br/>
<br/><a href="https://api.pennhillscdc.org/v1/mail/unsubscribe/[MAILCODE]/[ECID]" style="color:#FFFFFF"><u>unsubscribe</u></a>
</p>
</div>
</div>
</div>
</body>
</html>
END;
$email = str_replace('[NAME]', $m['fname'], $email);
$email = str_replace('[MAILCODE]', $m['mailcode'], $email);
$email = str_replace('[SUBJECT]', $body['subject'], $email);
$email = str_replace('[ECID]', $ecid, $email);
$email = str_replace('[BODY]', $body['body'], $email);
$email = str_replace('<img ', '<img style="max-width:100%" ', $email);
return $email;
}
function generatePassword($length = 10)
{
// Define the character sets to be used in the password
$uppercase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
$lowercase = 'abcdefghjkmnpqrstuvwxyz';
$numbers = '123456789';
$symbols = '!@#$%&_+?';
// Combine all character sets
$allCharacters = $uppercase . $lowercase . $numbers . $symbols;
// Initialize the password variable
$password = '';
// Generate the random password
for ($i = 0; $i < $length; $i++) {
$randomIndex = mt_rand(0, strlen($allCharacters) - 1);
$password .= $allCharacters[$randomIndex];
}
return $password;
}
function generateHash($length = 10)
{
// Define the character sets to be used in the password
$uppercase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
$lowercase = 'abcdefghjkmnpqrstuvwxyz';
$numbers = '123456789';
$symbols = '!@#$%&_+?';
// Combine all character sets
$allCharacters = $uppercase . $lowercase . $numbers;
// Initialize the password variable
$password = '';
// Generate the random password
for ($i = 0; $i < $length; $i++) {
$randomIndex = mt_rand(0, strlen($allCharacters) - 1);
$password .= $allCharacters[$randomIndex];
}
return $password;
}
function iso_date()
{
$milli = floor(microtime(true) * 1000) % 1000;
if ($milli < 10) {
$milli = '00' . $milli;
} else if ($milli < 100) {
$milli = '0' . $milli;
}
$now = date('c');
$now = str_replace('+00:00', '.' . $milli . 'Z', $now);
return $now;
}
function format_library_events_xml($xml)
{
$i = 0;
$events = [];
foreach ($xml as $event) {
foreach ($event as $k => $v) {
if ($k === 'title') {
$title = (string)$v;
$p1 = explode('--', $title);
$p2 = explode(' at ', $p1[1]);
$title = trim($p2[0]);
$events[$i]['title'] = $title;
} elseif ($k === 'ekdate') {
$events[$i]['ekdate'] = (string)$v;
$events[$i]['start_time'] = strtotime($events[$i]['ekdate']);
$events[$i]['start'] = str_ireplace('+00:00', '.000Z', date('c', $events[$i]['start_time']));
} elseif ($k === 'description') {
$desc = (string)$v;
$desc = mb_convert_encoding($desc, "HTML-ENTITIES", 'UTF-8');
//$desc = htmlallentities($desc);
$desc = str_replace('<p> </p>', '', $desc);
$desc = str_replace('Â', '', $desc);
$desc = str_replace('’', '\'', $desc);
$desc = str_replace('—', '—', $desc);
$desc = str_replace("\n", '—', $desc);
$desc = strip_tags($desc, '<p><a><br><u><i>');
$desc = preg_replace('/(<[^>]+) style=".*?"/i', '$1', $desc);
$desc = preg_replace('/<span[^>]+>/i', '$1', $desc);
$desc = preg_replace('/<\/span>/i', '$1', $desc);
//$desc = preg_replace('/<a[^>]+>/i', '$1', $events[$i]['description']);
//$desc = preg_replace('/<\/a>/i', '$1', $events[$i]['description']);
$desc = str_replace('Location:', '<b>LOCATION:</b>', $desc);
//$desc = str_replace('<a ', '<a target="_blank" ', $desc);
$events[$i]['description'] = $desc;
$events[$i]['private'] = (stristr($desc, 'private') ? 1 : 0);
} else if ($k === 'link') {
$events[$i]['ekid'] = substr($v, strpos($v, "#") + 1);
$events[$i][$k] = (string)$v;
} else if ($k === 'author') {
$author = (string)$v;
$email = preg_replace('/\(([^)]+)\)/i', '', $author);
$author = substr($v, strpos($v, "(") + 1, -1);
$events[$i]['description'] .= '<p><b>CONTACT:</b> ' . $author . ' - <a href="mailto:' . $email . '">' . $email . '</a></p>';
} else {
$events[$i][$k] = (string)$v;
}
}
$i++;
}
return $events;
}
function slugify($text, string $divider = '-')
{
// replace non letter or digits by divider
$text = preg_replace('~[^\pL\d]+~u', $divider, $text);
// transliterate
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
// trim
$text = trim($text, $divider);
// remove duplicate divider
$text = preg_replace('~-+~', $divider, $text);
// lowercase
$text = strtolower($text);
if (empty($text)) {
return 'n-a';
}
return $text;
}
function resize_crop_image($image_path, $output_path, $width, $height)
{
// Get image dimensions
list($original_width, $original_height) = getimagesize($image_path);
// Calculate aspect ratios
$original_aspect_ratio = $original_width / $original_height;
$desired_aspect_ratio = $width / $height;
// Determine cropping coordinates
if ($original_aspect_ratio > $desired_aspect_ratio) {
$new_width = $original_height * $desired_aspect_ratio;
$new_height = $original_height;
$x_offset = round(($original_width - $new_width) / 2);
$y_offset = 0;
} else {
$new_width = $original_width;
$new_height = $original_width / $desired_aspect_ratio;
$x_offset = 0;
$y_offset = round(($original_height - $new_height) / 2);
}
// Create image resources
$source_image = imagecreatefromstring(file_get_contents($image_path));
$destination_image = imagecreatetruecolor($width, $height);
// Resize and crop image
imagecopyresampled($destination_image, $source_image, 0, 0, round($x_offset), round($y_offset), round($width), round($height), round($new_width), round($new_height));
// Save image
imagejpeg($destination_image, $output_path);
// Clean up resources
imagedestroy($source_image);
imagedestroy($destination_image);
}
function formatPhoneNumber($number)
{
// Remove any non-numeric characters from the input number
$number = preg_replace('/\D/', '', $number);
// Check if the number is exactly 10 digits
if (strlen($number) == 10) {
// Format the number as xxx-xxx-xxxx
$formattedNumber = substr($number, 0, 3) . '-' . substr($number, 3, 3) . '-' . substr($number, 6, 4);
return $formattedNumber;
} else {
// If the number doesn't have 10 digits, return it as is
return $number;
}
}
function pull_facebook_images($items) {
foreach ($items as &$item) {
$images = [];
// Extract the media:content if it exists and add it to the images array
if (isset($item['media:content']) && !empty($item['media:content'])) {
$images[] = $item['media:content'];
}
// Capture all image URLs from the description
if (isset($item['description'])) {
$pattern = '/<img[^>]+src="([^"]+)"/i';
// Use preg_match_all to get all image src attributes
preg_match_all($pattern, $item['description'], $matches);
// Add the found image URLs to the images array
if (!empty($matches[1])) {
$images = array_merge($images, str_replace("&","&",$matches[1]));
}
// Remove the <img> tags from the description
//$item['description'] = preg_replace($pattern, '', $item['description']);
$item['description'] = trim(strip_tags($item['description']));
}
// Remove duplicate images
$images = array_unique($images);
// Add the images array to the item if there are any images
if (!empty($images)) {
$item['images'] = array_values($images); // Reset keys to be sequential
}
}
return $items;
}
function fetchXml($url)
{
// Fetch the XML content from the URL
$xmlContent = file_get_contents($url);
// Replace the invalid namespace with a valid one
$xmlContent = trim($xmlContent);
$xmlContent = str_replace(' xmlns="thumbnail"', '', $xmlContent);
$xmlContent = str_replace('<media', '<imgurl', $xmlContent);
$xmlContent = str_replace('270/170', '1620/1020', $xmlContent);
// Load the modified XML content
return simplexml_load_string($xmlContent, "SimpleXMLElement", LIBXML_NOCDATA);
}
function xmlToArray($xml)
{
$articles = [];
foreach ($xml->channel->item as $item) {
$article = [];
// Extract data for each article
$article['title'] = (string)$item->title;
// Extract image URL from <description> CDATA
$description = htmlspecialchars_decode((string)$item->description);
preg_match('/<img[^>]+src="([^"]+)"/', $description, $imageMatches);
$article['image_url'] = strlen((string)$item->imgurl) > 0 ? (string)$item->imgurl : (isset($imageMatches[1]) ? $imageMatches[1] : '');
// Extract text content from <description> CDATA
$descriptionText = strip_tags($description);
$article['description_text'] = $descriptionText;
// Extract text content from <dc:creator> CDATA
$article['creator'] = strlen((string)$item->children('dc', true)->creator) > 0 ? (string)$item->children('dc', true)->creator : (string)$item->author;
// Extract and format <pubDate> in ISO 8601 format
$pubDate = date_create_from_format('D, d M Y H:i:s O', (string)$item->pubDate);
$article['pubDate'] = $pubDate ? $pubDate->format('Y-m-d\TH:i:s.v\Z') : '';
// Extract <link>
$article['link'] = (string)$item->link;
// Extract <img>
//$article['imgurl'] = (string)$item->imgurl;
// Add article to the articles array
$articles[] = $article;
}
return $articles;
}
function extractImageUrl($content) {
// Use regex to extract the src attribute of the img tag
$matches = [];
preg_match('/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $matches);
// Return the first match or null if no match is found
return $matches[1] ?? null;
}
function fetchAndParseRSS($url) {
$config['useragent'] = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0';
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $url);
curl_setopt($handle, CURLOPT_FRESH_CONNECT, TRUE);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_USERAGENT, $config['useragent']);
curl_setopt($handle, CURLOPT_REFERER, 'https://www.phsd.org');
$response = curl_exec($handle);
curl_close($handle);
$xml = simplexml_load_string($response);
$json = json_encode($xml);
// Decode the JSON string to an associative array
$rssArray = json_decode($json, true);
// Extract image URLs from the 'content' field in each 'entry'
if (isset($rssArray['entry'])) {
// Handle the case where there is only one entry
if (isset($rssArray['entry'][0])) {
foreach ($rssArray['entry'] as &$entry) {
if (isset($entry['content'])) {
$entry['image_url'] = extractImageUrl($entry['content']);
}
}
} else {
// Handle the case where there is only one entry as an associative array
if (isset($rssArray['entry']['content'])) {
$rssArray['entry']['image_url'] = extractImageUrl($rssArray['entry']['content']);
}
}
}
return $rssArray;
}
function removeQueryParams($url) {
$pos = strpos($url, '?');
if ($pos !== false) {
return substr($url, 0, $pos);
}
return $url;
}
function formatHtml( $html, $strip_tags = false ) {
$allowed_tags = !is_bool($strip_tags) ? $strip_tags : null;
$html = html_entity_decode($html, ENT_QUOTES, 'UTF-8');
$html = str_replace(["’", "“", "”"], ["'", '"', '"'], $html);
if ($strip_tags) {
$html = strip_tags($html, $allowed_tags);
}
$html = addslashes(trim($html));
return $html;
}
function numeric($string) {
// Use preg_replace to remove all non-numeric characters
return preg_replace('/\D/', '', $string);
}
function convertToISO($dateString) {
// Try to create a DateTime object from the input date string
try {
$date = new DateTime($dateString, new DateTimeZone('UTC'));
} catch (Exception $e) {
// If the date string is invalid, return an error message
return 'Invalid date string';
}
// Format the date to the required ISO 8601 format with milliseconds and 'Z' for UTC
return $date->format('Y-m-d\TH:i:s.000\Z');
}
function convertUnixToISO($timestamp) {
// Create a DateTime object from the Unix timestamp
$date = new DateTime("@$timestamp");
// Set the timezone to UTC
$date->setTimezone(new DateTimeZone('UTC'));
// Format the date in ISO 8601 with milliseconds and Z suffix
return $date->format('Y-m-d\TH:i:s.v\Z');
}
function convertJoeDate($dateString) {
// Create a DateTime object from the input string
$date = new DateTime($dateString);
// Convert the date to the desired format and return it
return $date->format('Y-m-d\TH:i:s.000\Z');
}
function timestamp_to_gmt($date_in, $convert_local = true) {
if ($convert_local) {
// Create a DateTime object with the given date string in Eastern Time
$dateTime = new DateTime($date_in, new DateTimeZone('America/New_York'));
// Set the timezone to GMT
$dateTime->setTimezone(new DateTimeZone('GMT'));
}
else {
$dateTime = new DateTime($date_in);
}
// Format the date in the desired format (Y-m-d\TH:i:s.v\Z)
return $dateTime->format('Y-m-d\TH:i:s.v\Z');
}
function convert_timestamp_format($ical_timestamp) {
// Create a DateTime object from the iCal timestamp
$date = DateTime::createFromFormat('Ymd\THis\Z', $ical_timestamp, new DateTimeZone('UTC'));
// If the DateTime object was successfully created, format it to the desired format
if ($date) {
return $date->format('Y-m-d\TH:i:s.v\Z');
}
// Return null or an error message if the input format is incorrect
return null;
}
function removeAttributes($html) {
// Regular expression to match attributes in HTML tags
$pattern = '/(<[a-z][a-z0-9]*)(?:\s+[a-z\-]+\s*=\s*(?:"[^"]*"|\'[^\']*\'))+/i';
// Replace the matched attributes with just the tag name
$cleanedHtml = preg_replace($pattern, '$1', $html);
return $cleanedHtml;
}
function parse_ical($ical_string) {
$lines = explode("\n", $ical_string);
$events = [];
$current_event = null;
foreach ($lines as $line) {
$line = trim($line);
if ($line === "BEGIN:VEVENT") {
$current_event = [];
} elseif ($line === "END:VEVENT") {
if ($current_event) {
$events[] = $current_event;
}
$current_event = null;
} elseif ($current_event !== null) {
list($key, $value) = explode(":", $line, 2);
// Handle cases where value might continue on the next line (folding)
while (isset($lines[key($lines) + 1]) && strpos($lines[key($lines) + 1], ' ') === 0) {
$value .= "\n" . trim(next($lines));
}
$current_event[$key] = trim(stripcslashes($value));
}
}
return $events;
}
function getSchoolEvents( $id ) {
$local_file_path = '/home/awaldron/_phpassport/ical.dat';
$handle = curl_init();
$fp = fopen($local_file_path, 'w+'); // Open a file to write to
$url = 'https://www.phsd.org/calendar/calendar_'.$id.'_gmt.ics';
$useragent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0';
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $url);
curl_setopt($handle, CURLOPT_FRESH_CONNECT, TRUE);
curl_setopt($handle, CURLOPT_USERAGENT, $useragent);
curl_setopt($handle, CURLOPT_REFERER, 'https://www.phsd.org');
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
curl_setopt($handle, CURLOPT_FILE, $fp); // Write directly to file
curl_setopt($handle, CURLOPT_TIMEOUT, 60); // Set timeout
$success = curl_exec($handle);
curl_close($handle);
fclose($fp);
// Check if file has content
if (filesize($local_file_path) > 0) {
$response = file_get_contents($local_file_path);
$events = parse_ical( $response );
return $events;
} else {
return null;
}
}
function steelGoatDateRange() {
// Get the first day of the current month
$firstDay = date('Y-m-01'); // 'Y-m-01' gives the first day of the current month
// Get the last day of the third month from now
$lastDay = date('Y-m-t', strtotime('+3 months', strtotime($firstDay))); // 'Y-m-t' gives the last day of the month
// Return the two dates
return $firstDay.','.$lastDay;
}
/*
function xmlToArray($xml, $namespaces = [])
{
$array = [];
// Handle the attributes of the element
foreach ($xml->attributes() as $attrName => $attrValue) {
$array['@attributes'][$attrName] = (string)$attrValue;
}
// Handle the children of the element
foreach ($xml->children() as $childName => $child) {
$childArray = xmlToArray($child, $namespaces);
// If there are multiple children with the same name, group them in an array
if (isset($array[$childName])) {
if (!is_array($array[$childName]) || !isset($array[$childName][0])) {
$array[$childName] = [$array[$childName]];
}
$array[$childName][] = $childArray;
} else {
$array[$childName] = $childArray;
}
}
// Handle namespaced elements
foreach ($namespaces as $prefix => $namespace) {
foreach ($xml->children($namespace) as $childName => $child) {
$childArray = xmlToArray($child, $namespaces);
$array["{$prefix}:{$childName}"] = $childArray;
}
}
// Handle the value of the element (including CDATA)
$text = trim((string)$xml);
if (!empty($text) || empty($array)) {
$array['_value'] = $text;
}
// Handle CDATA
if ($xml->xpath('normalize-space()') && $xml->children()->count() == 0) {
$array['_cdata'] = (string)$xml;
}
// Separate description data
if (isset($array['_value']) || isset($array['_cdata'])) {
$htmlContent = isset($array['_cdata']) ? $array['_cdata'] : $array['_value'];
$array['description_text'] = strip_tags($htmlContent);
// Extract image URLs
preg_match_all('/<img[^>]+src="([^">]+)"/i', $htmlContent, $matches);
$array['image_urls'] = $matches[1];
}
return $array;
}
*/
function parseAgenda($html) {
$eventsList = []; // Flat associative array for all events
$currentYear = date('Y'); // Default year
// Match all month and day blocks
preg_match_all('/<div class="agenda-month">(.*?)<\/div>(.*?)((<div class="agenda-month">)|$)/s', $html, $monthBlocks);
// Loop through each month block
foreach ($monthBlocks[1] as $index => $monthName) {
$month = trim($monthName);
$daysHtml = $monthBlocks[2][$index];
// Extract all agenda-day blocks for this month
preg_match_all('/<div class="agenda-day">(.*?)<\/div>/s', $daysHtml, $dayMatches);
foreach ($dayMatches[1] as $dayBlock) {
// Extract the day (e.g. "Mon, 21")
preg_match('/<i>(.*?)<\/i>/', $dayBlock, $dateMatch);
$dayText = isset($dateMatch[1]) ? trim($dateMatch[1]) : null;
// Parse day and month to get the full date in ISO format
if ($dayText && preg_match('/\w+, (\d+)/', $dayText, $dayParts)) {
$day = $dayParts[1];
$monthNum = date('m', strtotime($month)); // Convert month name to number
$dateISO = sprintf('%s-%02d-%02dT00:00:00.000Z', $currentYear, $monthNum, $day);
} else {
continue; // Skip if we can't parse the date
}
// Extract all events within that day
preg_match_all('/<div class="agenda-event">(.*?)<\/div>/s', $dayBlock, $eventMatches);
foreach ($eventMatches[1] as $eventBlock) {
// Extract the event time from the <b> tag (append time to dateISO if needed)
preg_match('/<b>(.*?)<\/b>/s', $eventBlock, $timeMatch);
$time = isset($timeMatch[1]) ? trim($timeMatch[1]) : null;
// Append time to ISO date if available
if ($time) {
$time24hr = date('H:i:s', strtotime($time)); // Convert to 24-hour format
$dateISO = sprintf('%s-%02d-%02dT%s.000Z', $currentYear, $monthNum, $day, $time24hr);
}
// Extract the event title from the <span> tag
preg_match('/<span>(.*?)<\/span>/s', $eventBlock, $titleMatch);
$title = isset($titleMatch[1]) ? trim($titleMatch[1]) : null;
// Extract the event URL from the <a> tag
preg_match('/<a href="(.*?)"/s', $eventBlock, $urlMatch);
$url = isset($urlMatch[1]) ? trim($urlMatch[1]) : null;
// Add the event to the flat list
if ($title && $url) {
$eventsList[] = [
'date' => $dateISO,
'title' => $title,
'url' => $url,
];
}
}
}
}
return $eventsList;
}
function savvy_description( $url ) {
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $url);
curl_setopt($handle, CURLOPT_FRESH_CONNECT, TRUE);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($handle);
curl_close($handle);
$linesArray = preg_split('/\r\n|\r|\n/', $response);
$found_description = false;
foreach ($linesArray as $line) {
if ($found_description) {
return trim($line);
} else if (strstr($line, '<b>Details</b>')) {
$found_description = true;
}
}
}
//function savvy_format( $url ) {
// $handle = curl_init();
// curl_setopt($handle, CURLOPT_URL, $url);
// curl_setopt($handle, CURLOPT_FRESH_CONNECT, TRUE);
// curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
// $response = curl_exec($handle);
// curl_close($handle);
//
// $linesArray = preg_split('/\r\n|\r|\n/', $response);
//
// $currMonth = '';
// $currYear = '';
// $currDate = '';
// $currTime = '';
// $currUrl = '';
// $currTitle = '';
//
// $events = [];
// $in_event = false;
// $debug = false;
//
// foreach ($linesArray as $line) {
// if (strstr($line, 'agenda-month')) {
// $month_year = explode(' ',strip_tags(trim($line)));
// $currMonth = $month_year[0];
// $currYear = $month_year[1];
// if ($debug) echo "\n".$currMonth. ' '. $currYear;
// }
// if (strstr($line, '</i>')) {
// $text = trim(strip_tags($line));
// if (preg_match('/^([A-Za-z]{3}),\s*(\d{1,2})$/', $text, $m)) {
// $currDay = $m[1];
// $currDate = $m[2];
// if ($debug) echo "\n".$currDay.' '.$currDate;
// }
// }
// if ($in_event) {
// if (strstr($line, '</b>')) {
// $currTime = trim(strip_tags($line));
// if ($debug) echo "\n".$currTime;
// }
// if (strstr($line, '<a href=')) {
// preg_match('/<a\s+href=["\']([^"\']+)["\']/', $line, $matches);
// $currUrl = $matches[1] ?? null;
// if ($debug) echo "\n".$currUrl;
// }
// if (strstr($line, '<span>')) {
// $currTitle = str_replace(' Agenda', '', trim(strip_tags(trim($line))));
// if ($debug) echo "\n".$currTitle;
// switch ($currTitle) {
// case 'Council Meeting': $currTitle = 'Penn Hills Council Meeting'; break;
// case 'Zoning Hearing Board Meeting': $currTitle = 'Penn Hills Zoning Board Meeting'; break;
// case 'Planning Commission Meeting': $currTitle = 'Penn Hills Planning Commission Meeting'; break;
// case 'Non-Voting Meeting': $currTitle = 'Penn Hills Council Non-Voting Meeting'; break;
// }
// }
// if (strstr($line, '</div>')) {
// if (!strstr($currTitle, 'Library') && !strstr($currTitle, 'Agenda')) {
// $events[] = array (
// 'month' => $currMonth,
// 'date' => $currDate,
// 'year' => $currYear,
// 'time' => $currTime,
// 'url' => $currUrl,
// 'title' => $currTitle,
// );
// if ($debug) echo "\n".'-> Event Added!';
// } else {
// if ($debug) echo "\n".'-> Event IGNORED';
// }
// if ($debug) echo "\n".'------------------';
// $in_event = false;
// }
// }
// if (strstr($line, 'agenda-event')) {
// $in_event = true;
// }
// }
// return $events;
//}
function savvy_format($url) {
libxml_use_internal_errors(true);
// Fetch HTML
$html = file_get_contents($url);
if ($html === false) {
return [];
}
// Load DOM
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$events = [];
$currMonth = '';
$currYear = '';
$currDate = '';
// Get all agenda blocks in document order
$nodes = $xpath->query(
'//div[contains(@class,"agenda-month")] |
//div[contains(@class,"agenda-day")]'
);
foreach ($nodes as $node) {
/* ===========================
Month header
=========================== */
if (strpos($node->getAttribute('class'), 'agenda-month') !== false) {
$text = trim($node->textContent);
[$currMonth, $currYear] = explode(' ', $text, 2);
}
/* ===========================
Day block
=========================== */
if (strpos($node->getAttribute('class'), 'agenda-day') !== false) {
// Extract date
$dateNode = $xpath->query('.//i', $node)->item(0);
if (!$dateNode) continue;
// "Wed, 28"
if (!preg_match('/^[A-Za-z]{3},\s*(\d{1,2})$/', trim($dateNode->textContent), $m)) {
continue;
}
$currDate = $m[1];
// Loop events for this day
$eventNodes = $xpath->query('.//div[contains(@class,"agenda-event")]', $node);
foreach ($eventNodes as $eventNode) {
$titleNode = $xpath->query('.//span', $eventNode)->item(0);
$timeNode = $xpath->query('.//b', $eventNode)->item(0);
$linkNode = $xpath->query('.//a', $eventNode)->item(0);
if (!$titleNode || !$timeNode || !$linkNode) continue;
$title = trim(str_replace(' Agenda', '', $titleNode->textContent));
$time = trim($timeNode->textContent);
$url = $linkNode->getAttribute('href');
// Normalize titles (your original logic)
switch ($title) {
case 'Council Meeting':
$title = 'Penn Hills Council Meeting';
break;
case 'Zoning Hearing Board Meeting':
$title = 'Penn Hills Zoning Board Meeting';
break;
case 'Planning Commission Meeting':
$title = 'Penn Hills Planning Commission Meeting';
break;
case 'Non-Voting Meeting':
$title = 'Penn Hills Council Non-Voting Meeting';
break;
}
// Ignore library events
if (str_contains($title, 'Library')) {
continue;
}
$events[] = [
'month' => $currMonth,
'date' => $currDate,
'year' => $currYear,
'time' => $time,
'url' => $url,
'title' => $title,
];
}
}
}
return $events;
}
function savvy_dates($eventData) {
if (empty($eventData['time'])) {
$eventData['time'] = '12am';
}
// Check if all necessary date components are available
if (!isset($eventData['month'], $eventData['date'], $eventData['year'], $eventData['time'])) {
return "Incomplete date information!";
}
// Create a string combining the date and time parts
$dateString = $eventData['month'] . ' ' . $eventData['date'] . ', ' . $eventData['year'] . ' ' . $eventData['time'];
// Create a DateTime object from the string in the Eastern Time Zone
$startDate = DateTime::createFromFormat('F j, Y ga', trim($dateString), new DateTimeZone('America/New_York'));
if (!$startDate) {
return "Invalid date or time format! - " . $dateString;
}
// Convert the start DateTime object to UTC (Zulu time)
$startDate->setTimezone(new DateTimeZone('UTC'));
// Clone the start date to create the end date
$endDate = clone $startDate;
// Add 1.5 hours to the end date
$endDate->modify('+1 hour 30 minutes');
// Return both start and end times in ISO 8601 Zulu format
return [
'start' => $startDate->format('Y-m-d\TH:i:s.000\Z'),
'end' => $endDate->format('Y-m-d\TH:i:s.000\Z'),
'allday' => $eventData['time'] === '12am' ? 1 : 0
];
}
function getDayStartAndEnd($timezone = 'America/New_York') {
// Create DateTime objects for start and end of the day
$date = new DateTime('now', new DateTimeZone($timezone));
// Start of the day
$startOfDay = clone $date;
$startOfDay->setTime(0, 0, 0);
$startOfDayUtc = clone $startOfDay;
$startOfDayUtc->setTimezone(new DateTimeZone('UTC'));
$startFormatted = $startOfDayUtc->format('Y-m-d\TH:i:s.v\Z');
// End of the day
$endOfDay = clone $date;
$endOfDay->setTime(23, 59, 59);
$endOfDayUtc = clone $endOfDay;
$endOfDayUtc->setTimezone(new DateTimeZone('UTC'));
$endFormatted = $endOfDayUtc->format('Y-m-d\TH:i:s.v\Z');
return [
'start' => $startFormatted,
'end' => $endFormatted
];
}
function chatgpt_post_title($text) {
$api_key = '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'; // Replace with your OpenAI API Key
$api_url = 'https://api.openai.com/v1/chat/completions';
$data = [
'model' => 'gpt-4', // Use gpt-4 or gpt-3.5-turbo for cheaper rates
'messages' => [
['role' => 'system', 'content' => 'Summarize the input into a concise, engaging title under 100 characters. Ignore mentioning number of fire stations responding to the incident.'],
['role' => 'user', 'content' => $text]
],
'max_tokens' => 50, // Limits response length
'temperature' => 0.9 // Adjust for creativity
];
$headers = [
"Content-Type: application/json",
"Authorization: Bearer $api_key"
];
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
return $result['choices'][0]['message']['content'] ?? 'Error generating title';
}
function save_facebook_image($originalUrl) {
$path = parse_url($originalUrl, PHP_URL_PATH);
$filename = basename($path);
$savePath = "/home/awaldron/_phpassport/pennhillspassport.com/fb/" . $filename;
$publicUrl = "https://pennhillspassport.com/fb/" . $filename;
// Check if the image already exists
if (file_exists($savePath)) {
return $publicUrl;
}
// Use the original Facebook URL to download the image
$ch = curl_init($originalUrl);
$fp = fopen($savePath, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
$success = curl_exec($ch);
curl_close($ch);
fclose($fp);
if ($success) {
return $publicUrl;
} else {
return false;
}
}