log_file = fopen("./scripts/decweb_synchronization.log", "w+"); if ($this->log_file === false) { error_log("DecWebSync : Unable to open log file"); exit(); } $this->log(str_repeat("*", 50)); // Get Settings $this->settings = new SimpleXMLElement((file_exists("./xml/settings.xml") ? file_get_contents("./xml/settings.xml") : "")); if ($this->settings->count() === 0) { error_log("DecWebSync : Unable to initialize settings"); exit(); } // Open Database Connections $this->decweb_connection = $this->connect_decweb(); $this->realestatedatainc_connection = $this->connect_realestatedatainc(); } // ============================ // Synchronize Database Process // ============================ public function process() { $this->log(date("Y-m-d H:i:s") . " - Starting Decweb Synchronization"); $this->download_access_file(); $this->process_access_file(); $this->clean_up_sqls(); $this->clean_up_mdb(); $this->migrateTables(); $this->email_admins("Database Reload Status", "Database Reload from Access(MDB) file to SQL was successful. See attached logfile for details."); $this->sendWebHookAlert("Database Reload Status", "Database Reload from Access(MDB) file to SQL was successful."); $this->log(date("Y-m-d H:i:s") . " - Synchronization Complete"); exit(); } // ========================================== // Return a DecWeb MySQL Database Connection. // ========================================== public function connect_decweb() { try { $host = (string) ($this->settings->DECDatabaseServer ?? ""); $database = (string) ($this->settings->DECDatabase ?? ""); $user = (string) ($this->settings->DECDatabaseUser ?? ""); $password = (string) ($this->settings->DECDatabasePassword ?? ""); $connection = new PDO("mysql:host={$host};dbname={$database};charset=utf8", $user, $password); $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException | Exception $e) { $this->log($e->getMessage()); exit(); } return $connection; } // ==================================================== // Return a RealEstateDataInc MySQL Database Connection // ==================================================== public function connect_realestatedatainc() { try { $host = (string) ($this->settings->DatabaseServer ?? ""); $database = (string) ($this->settings->DatabaseName ?? ""); $user = (string) ($this->settings->DatabaseUser ?? ""); $password = (string) ($this->settings->DatabasePassword ?? ""); $connection = new PDO("mysql:host={$host};dbname={$database};charset=utf8", $user, $password); $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException | Exception $e) { $this->log($e->getMessage()); exit(); } return $connection; } private function download_access_file() { $this->log("Starting Access File Download"); $ftp_username = (string) $this->settings->DecFtpServerUser; $ftp_password = (string) $this->settings->DecFtpServerPassword; $ftp_url = (string) $this->settings->DecFtpServerURL; $ftp_port = (string) $this->settings->DecFtpServerPort; exec("curl -O -u {$ftp_username}:{$ftp_password} ftp://{$ftp_url}:{$ftp_port}/{$this->ftp_access_file}"); exec("mv {$this->ftp_access_file} data/."); $this->log("Completed Access File Download"); } // ============================= // Log a message to the Log File // ============================= private function log($message = "") { fwrite($this->log_file, date("Y-m-d H:i A") . " : " . "{$message} \n"); } // ============================== // Process Downloaded Access File // ============================== private function process_access_file() { $this->log("Processing Dec Web Access File"); // ------------------------------------------- // Export Data from Access Tables to SQL files // ------------------------------------------- $this->log("Export Data from Access Tables to SQL Files"); $allowed_tables = [ 'BizFacts_tbl', 'PermitFacts_tbl', 'PurchasedServices_tbl', 'PZ_tbl', 'Users_tbl', ]; // mdb-tables outputs a space-separated list with newlines; normalize then split $raw = shell_exec("mdb-tables -1 ./data/DEC_Web.mdb 2>/dev/null"); if ($raw === null) { throw new RuntimeException("mdb-tables failed or returned null output."); } $access_tables = array_values(array_filter(array_map('trim', preg_split('/\R+/', trim($raw))))); // Export only allowed tables that exist in the MDB foreach (array_intersect($access_tables, $allowed_tables) as $access_table) { $outFile = rtrim($this->sql_directory, '/') . "/{$access_table}.sql"; $shell_cmd = sprintf( 'mdb-export -D "%%Y-%%m-%%d %%H:%%M:%%S" -H -I mysql %s %s > %s 2>&1', escapeshellarg('./data/DEC_Web.mdb'), escapeshellarg($access_table), escapeshellarg($outFile) ); $output = []; $rc = 0; exec($shell_cmd, $output, $rc); if ($rc !== 0) { $this->log("ERROR exporting {$access_table}: " . implode(" | ", $output)); throw new RuntimeException("mdb-export failed for {$access_table} (rc={$rc})."); } $this->log("Exported {$access_table} -> {$outFile}"); } // ---------------- // Import SQL files // ---------------- $this->log("Import SQL Files"); // Only import allowed tables (avoid importing random files in that folder) foreach ($allowed_tables as $table) { $file = rtrim($this->sql_directory, '/') . "/{$table}.sql"; if (!is_readable($file)) { $this->log("Skipping missing/unreadable SQL file: {$file}"); continue; } // Hardening: table identifier validation (extra safety) if (!preg_match('/^[A-Za-z0-9_]+$/', $table)) { throw new RuntimeException("Invalid table name: {$table}"); } $this->log("Truncating {$table}"); $this->decweb_connection->exec("TRUNCATE TABLE `{$table}`"); $this->log("Importing {$table} from {$file}"); $fh = fopen($file, 'rb'); if ($fh === false) { throw new RuntimeException("Unable to open file: {$file}"); } $this->decweb_connection->beginTransaction(); try { $buffer = ''; while (($line = fgets($fh)) !== false) { $trim = trim($line); // Skip common dump comments/blank lines if ($trim === '' || str_starts_with($trim, '--') || str_starts_with($trim, '#')) { continue; } $buffer .= $line; // Execute each statement as it completes if (str_ends_with(rtrim($trim), ';')) { $this->decweb_connection->exec($buffer); $buffer = ''; } } if (trim($buffer) !== '') { $this->decweb_connection->exec($buffer); } fclose($fh); $this->decweb_connection->commit(); $this->log("Imported {$table}"); } catch (Throwable $e) { fclose($fh); $this->decweb_connection->rollBack(); $this->log("ERROR importing {$table}: " . $e->getMessage()); throw $e; } } $this->log("Completed Processing SQL Files"); } // ================== // Clean Up SQL Files // ================== public function clean_up_sqls() { $this->log("Cleaning Up..."); $sql_files = scandir($this->sql_directory); $sql_files = array_diff($sql_files, array('.', '..')); foreach ($sql_files as $file) { $filename = "{$this->sql_directory}/{$file}"; unlink($filename); } $this->log("Finished Cleaning Up..."); } // ================== // Clean Up MDB Files // ================== public function clean_up_mdb() { $this->log("Cleaning Up MDB's..."); $datetime = date("Ymd_His"); $currentFile = "{$this->data_directory}/DEC_Web.mdb"; $archivedFile = "{$this->data_directory}/DEC_Web_{$datetime}.mdb"; // Rename latest MDB file if it exists if (file_exists($currentFile)) { if (!rename($currentFile, $archivedFile)) { $this->log("Error renaming DEC_Web.mdb."); return false; } } else { $this->log("DEC_Web.mdb does not exist. Nothing to rename."); } // Keep only the latest 5 MDB files $maxfiles = 5; $mdb_files = glob("{$this->data_directory}/*.mdb"); usort($mdb_files, function ($a, $b) { return filemtime($a) <=> filemtime($b); }); $filesToDelete = count($mdb_files) - $maxfiles; if ($filesToDelete > 0) { for ($i = 0; $i < $filesToDelete; $i++) { if (unlink($mdb_files[$i])) { $this->log("Deleted: " . basename($mdb_files[$i])); } else { $this->log("Error deleting: " . basename($mdb_files[$i])); } } } $this->log("Finished Cleaning Up MDB's..."); return true; } // ========================== // Send an Alert/Notification // ========================== public function sendWebHookAlert($message, $title = "🚨 Server Alert", $alerttype = "1fff00") { $webhookUrl = $this->settings->DiscordWebHookURL; $data = [ 'embeds' => [[ 'title' => $title, 'description' => $message, 'color' => hexdec($alerttype), 'footer' => [ 'text' => 'RDI System Notificiation', ], 'timestamp' => date('c'), // ISO 8601 format ]] ]; $jsonData = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $ch = curl_init($webhookUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); $response = curl_exec($ch); if ($response === false) { $this->log('Curl error: ' . curl_error($ch)); } curl_close($ch); } // ============ // Email Admins // ============ public function email_admins($subject = "", $message = "") { $this->log(date("Y-m-d H:i:s") . " - Emailing Admins..."); $settings = new SimpleXMLElement((file_exists("./xml/settings.xml") ? file_get_contents("./xml/settings.xml") : "")); // Server Variables $host = $settings->SmtpServer; $username = $settings->SmtpUserName; $password = $settings->SmtpPassword; // Recipient Variables $from_email = $settings->FromEmail; $from_name = $settings->FromEmail['name']; $support_email = $settings->SupportEmail; $support_name = $settings->SupportEmail['name']; if ($settings->Environment == 'Production') { $admin_email = $settings->AdminEmail; $admin_name = $settings->AdminEmail['name']; } // Content Variables $subject = "Database Reload Status"; $attachment_name = "./scripts/decweb_synchronization.log"; $mail = new PHPMailer(true); try { //Server settings $mail->isSMTP(); $mail->Host = $host; $mail->SMTPAuth = true; $mail->Username = $username; $mail->Password = $password; $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; $mail->Port = 465; //Recipients $mail->setFrom($from_email, $from_name); $mail->addAddress($support_email, $support_name); if ($settings->Environment == 'Production') { $mail->addAddress($admin_email, $admin_name); } //Attachments $mail->addAttachment($attachment_name); //Content $mail->isHTML(true); $mail->Subject = $subject; $mail->Body = $message; $mail->send(); } catch (Exception $e) { $this->log("Message could not be sent. Mailer Error: {$mail->ErrorInfo}"); } } // ============================================ // Migrate Tables to RealEstateDataInc Database // ============================================ public function migrateTables() { $this->log("Migrating Tables to RealEstateDataInc Database for New Interface..."); $this->migrateUsers(); $this->migratePurchasedServices(); $this->migratePermitFacts(); $this->migrateBusinessFacts(); $this->migratePZFacts(); $this->log("Finished Migrating Tables..."); } // ============= // Migrate Users // ============= private function migrateUsers(): void { $this->log("Processing decweb Users"); $users = $this->getUsers(); $insertSQL = "INSERT INTO users ( user_id, user_name, user_email, user_password, user_role, user_change_password, user_active, user_creator, user_created ) VALUES ( :user_id, :user_name, :user_email, :user_password, :user_role, :user_change_password, :user_active, :user_creator, :user_created )"; $updateSQL = "UPDATE users SET user_name = :user_name, user_email = :user_email, user_password = :user_password, user_role = :user_role, user_change_password = :user_change_password, user_active = :user_active, user_changer = :user_changer, user_changed = :user_changed WHERE user_id = :user_id"; $insertStmt = $this->realestatedatainc_connection->prepare($insertSQL); $updateStmt = $this->realestatedatainc_connection->prepare($updateSQL); $counter = 0; foreach ($users as $user) { $user_id = (string) (trim($user['UserName']) ?? ''); $user_name = (string) (trim($user['UserName']) ?? ''); $user_email = (string) (trim($user['Email']) ?? ''); $raw_password = (string) ($user['Password'] ?? ''); $user_password = password_hash($raw_password, PASSWORD_DEFAULT); $user_role = "User"; if ($user_name == 'D002116') { $user_role = "Administrator"; } // normalize to 0/1 from "Active"/anything else $user_active = (int) (strcasecmp(trim((string) ($user['AcctStatus'] ?? '')), 'Active') === 0); $user_change_password = 0; $now = date("Y-m-d H:i:s"); $exists = $this->userExists($user_id); if ($exists) { $statement = $updateStmt; // update-only audit fields $statement->bindValue(':user_changer', 'Migrated'); $statement->bindValue(':user_changed', $now); } else { $statement = $insertStmt; // insert-only audit fields $statement->bindValue(':user_creator', 'Migrated'); $statement->bindValue(':user_created', $now); } // shared bindings $statement->bindValue(':user_id', $user_id); $statement->bindValue(':user_name', $user_name); $statement->bindValue(':user_email', $user_email); $statement->bindValue(':user_password', $user_password); $statement->bindValue(':user_role', $user_role); $statement->bindValue(':user_change_password', $user_change_password, PDO::PARAM_INT); $statement->bindValue(':user_active', $user_active, PDO::PARAM_INT); $statement->execute(); $counter++; } $this->log("Processed {$counter} User records..."); $this->log("Completed migrating {$counter} User records."); } // ===================== // Return a Users Object // ===================== private function getUsers() { $SQL = "select * from Users_tbl"; $users = $this->decweb_connection->query($SQL)->fetchAll(PDO::FETCH_ASSOC); return $users; } // ===================== // Return a Users Object // ===================== private function userExists($username) { $SQL = "SELECT user_id FROM view_users WHERE user_id = :username LIMIT 1"; $statement = $this->realestatedatainc_connection->prepare($SQL); $statement->bindValue(':username', $username, PDO::PARAM_STR); $statement->execute(); $user = $statement->fetch(PDO::FETCH_ASSOC); return ($user !== false); } // ============= // Migrate Users // ============= private function migratePermitFacts(): void { $this->log("Migrating Permit Facts from decweb"); $this->realestatedatainc_connection->exec("TRUNCATE TABLE permitfacts"); $permitfacts = $this->getPermitFacts(); $SQL = "INSERT INTO permitfacts ( permitfact_legacy_id, permitfact_entry_date, permitfact_permit_number, permitfact_permit_date, permitfact_county, permitfact_project_address, permitfact_project_name, permitfact_project_type, permitfact_project_city, permitfact_project_state, permitfact_project_zip, permitfact_company, permitfact_address, permitfact_city, permitfact_state, permitfact_zip, permitfact_phone, permitfact_size, permitfact_value, permitfact_work_type, permitfact_building_type, permitfact_lot_block, permitfact_dist_ll, permitfact_owner_name, permitfact_owner_address, permitfact_owner_city, permitfact_owner_state, permitfact_owner_zip, permitfact_owner_phone, permitfact_creator, permitfact_created ) VALUES ( :permitfact_legacy_id, :permitfact_entry_date, :permitfact_permit_number, :permitfact_permit_date, :permitfact_county, :permitfact_project_address, :permitfact_project_name, :permitfact_project_type, :permitfact_project_city, :permitfact_project_state, :permitfact_project_zip, :permitfact_company, :permitfact_address, :permitfact_city, :permitfact_state, :permitfact_zip, :permitfact_phone, :permitfact_size, :permitfact_value, :permitfact_work_type, :permitfact_building_type, :permitfact_lot_block, :permitfact_dist_ll, :permitfact_owner_name, :permitfact_owner_address, :permitfact_owner_city, :permitfact_owner_state, :permitfact_owner_zip, :permitfact_owner_phone, :permitfact_creator, :permitfact_created ) "; $statement = $this->realestatedatainc_connection->prepare($SQL); $counter = 0; foreach ($permitfacts as $permitfact) { $permitfact_legacy_id = $permitfact['PermitFactsID'] ?? null; $permitfact_entry_date = $permitfact['EntryDate'] ?? null; $permitfact_permit_number = $permitfact['PermitNum'] ?? null; $permitfact_permit_date = $permitfact['PermitDate'] ?? null; $permitfact_county = $this->getCountyByCountyName($permitfact['County']) ?? null; $permitfact_project_address = $permitfact['ProjectAddr'] ?? null; $permitfact_project_name = $permitfact['ProjectName'] ?? null; $permitfact_project_type = $this->getDropdownSerialByValue($permitfact['ProjectType'], 3) ?? null; $permitfact_project_city = $this->getCityByCityName($permitfact['ProjectCity']) ?? null; $permitfact_project_state = $permitfact['ProjectState'] ?? null; $permitfact_project_zip = $permitfact['ProjectZip'] ?? null; $permitfact_company = $permitfact['Company'] ?? null; $permitfact_address = $permitfact['Address'] ?? null; $permitfact_city = $this->getCityByCityName($permitfact['City']) ?? null; $permitfact_state = $permitfact['State'] ?? null; $permitfact_zip = $permitfact['Zip'] ?? null; $permitfact_phone = $this->normailizePhoneNumber($permitfact['Phone']) ?? null; $permitfact_size = $permitfact['Size'] ?? null; $permitfact_value = $permitfact['Value'] ?? null; $permitfact_work_type = $this->getDropdownSerialByValue($permitfact['WorkType'], 4) ?? null; $permitfact_building_type = $this->getDropdownSerialByValue($permitfact['BldgType'], 5) ?? null; $permitfact_lot_block = $permitfact['Lot_BLK'] ?? null; $permitfact_dist_ll = $permitfact['Dist_LL'] ?? null; $permitfact_owner_name = $permitfact['OwnerName'] ?? null; $permitfact_owner_address = $permitfact['OwnerAddress'] ?? null; $permitfact_owner_city = $this->getCityByCityName($permitfact['OwnerCity']) ?? null; $permitfact_owner_state = $permitfact['OwnerState'] ?? null; $permitfact_owner_zip = $permitfact['OwnerZip'] ?? null; $permitfact_owner_phone = $this->normailizePhoneNumber($permitfact['OwnerPhone']) ?? null; $statement->bindValue(':permitfact_legacy_id', $permitfact_legacy_id, PDO::PARAM_STR); $statement->bindValue(':permitfact_entry_date', $permitfact_entry_date, PDO::PARAM_STR); $statement->bindValue(':permitfact_permit_number', $permitfact_permit_number, PDO::PARAM_STR); $statement->bindValue(':permitfact_permit_date', $permitfact_permit_date, PDO::PARAM_STR); $statement->bindValue(':permitfact_county', $permitfact_county, PDO::PARAM_INT); $statement->bindValue(':permitfact_project_address', $permitfact_project_address, PDO::PARAM_STR); $statement->bindValue(':permitfact_project_name', $permitfact_project_name, PDO::PARAM_STR); $statement->bindValue(':permitfact_project_type', $permitfact_project_type, PDO::PARAM_INT); $statement->bindValue(':permitfact_project_city', $permitfact_project_city, PDO::PARAM_INT); $statement->bindValue(':permitfact_project_state', $permitfact_project_state, PDO::PARAM_STR); $statement->bindValue(':permitfact_project_zip', $permitfact_project_zip, PDO::PARAM_STR); $statement->bindValue(':permitfact_company', $permitfact_company, PDO::PARAM_STR); $statement->bindValue(':permitfact_address', $permitfact_address, PDO::PARAM_STR); $statement->bindValue(':permitfact_city', $permitfact_city, PDO::PARAM_INT); $statement->bindValue(':permitfact_state', $permitfact_state, PDO::PARAM_STR); $statement->bindValue(':permitfact_zip', $permitfact_zip, PDO::PARAM_STR); $statement->bindValue(':permitfact_phone', $permitfact_phone, PDO::PARAM_STR); $statement->bindValue(':permitfact_size', $permitfact_size, PDO::PARAM_INT); $statement->bindValue(':permitfact_value', $permitfact_value, PDO::PARAM_STR); $statement->bindValue(':permitfact_work_type', $permitfact_work_type, PDO::PARAM_INT); $statement->bindValue(':permitfact_building_type', $permitfact_building_type, PDO::PARAM_INT); $statement->bindValue(':permitfact_lot_block', $permitfact_lot_block, PDO::PARAM_STR); $statement->bindValue(':permitfact_dist_ll', $permitfact_dist_ll, PDO::PARAM_STR); $statement->bindValue(':permitfact_owner_name', $permitfact_owner_name, PDO::PARAM_STR); $statement->bindValue(':permitfact_owner_address', $permitfact_owner_address, PDO::PARAM_STR); $statement->bindValue(':permitfact_owner_city', $permitfact_owner_city, PDO::PARAM_INT); $statement->bindValue(':permitfact_owner_state', $permitfact_owner_state, PDO::PARAM_STR); $statement->bindValue(':permitfact_owner_zip', $permitfact_owner_zip, PDO::PARAM_STR); $statement->bindValue(':permitfact_owner_phone', $permitfact_owner_phone, PDO::PARAM_STR); $statement->bindValue(':permitfact_creator', 'Migration', PDO::PARAM_STR); $statement->bindValue(':permitfact_created', date("Y-m-d"), PDO::PARAM_STR); $statement->execute(); $counter++; } $this->log("Processed $counter Permit Fact records... "); $this->log("Migration of Permit Facts Completed"); } // ====================== // Migrate Business Facts // ====================== private function migrateBusinessFacts(): void { $this->log("Migrating Business Facts from decweb"); $this->realestatedatainc_connection->exec("TRUNCATE TABLE businessfacts"); $businessfacts = $this->getBusinessFacts(); $SQL = "INSERT INTO businessfacts ( businessfact_legacy_id, businessfact_company, businessfact_address_one, businessfact_address_two, businessfact_city, businessfact_state, businessfact_zip, businessfact_county, businessfact_phone, businessfact_fax, businessfact_email_address, businessfact_business_class, businessfact_action, businessfact_employee_count, businessfact_inception, businessfact_sic_code, businessfact_contact_name, businessfact_contact_title, businessfact_license_date, businessfact_home_based_business, businessfact_year_established, businessfact_entry_date, businessfact_square_feet, businessfact_source_lease_ending, businessfact_lease_end, businessfact_creator, businessfact_created ) VALUES ( :businessfact_legacy_id, :businessfact_company, :businessfact_address_one, :businessfact_address_two, :businessfact_city, :businessfact_state, :businessfact_zip, :businessfact_county, :businessfact_phone, :businessfact_fax, :businessfact_email_address, :businessfact_business_class, :businessfact_action, :businessfact_employee_count, :businessfact_inception, :businessfact_sic_code, :businessfact_contact_name, :businessfact_contact_title, :businessfact_license_date, :businessfact_home_based_business, :businessfact_year_established, :businessfact_entry_date, :businessfact_square_feet, :businessfact_source_lease_ending, :businessfact_lease_end, :businessfact_creator, :businessfact_created )"; $statement = $this->realestatedatainc_connection->prepare($SQL); $counter = 0; foreach ($businessfacts as $businessfact) { $businessfact_legacy_id = $businessfact['BizFactsRecordID'] ?? null; $businessfact_company = $businessfact['Company'] ?? null; $businessfact_address_one = $businessfact['Address1'] ?? null; $businessfact_address_two = $businessfact['Address2'] ?? null; $businessfact_city = $this->getCityByCityName($businessfact['City']) ?? null; $businessfact_state = $businessfact['State'] ?? null; $businessfact_zip = $businessfact['Zip'] ?? null; $businessfact_county = $this->getCountyByCountyName($businessfact['County']) ?? null; $businessfact_phone = $this->normailizePhoneNumber($businessfact['Phone']) ?? null; $businessfact_fax = $this->normailizePhoneNumber($businessfact['Fax']) ?? null; $businessfact_email_address = $this->normailizeEmailAddress($businessfact['EmailAddress']) ?? null; $businessfact_business_class = $this->getDropdownSerialByValue($businessfact['BC'], 6) ?? null; $businessfact_action = $this->getDropdownSerialByValue($businessfact['Description'], 7) ?? null; $businessfact_employee_count = $businessfact['ES'] ?? null; $businessfact_inception = $businessfact['BizInception'] ?? null; $businessfact_sic_code = $businessfact['SIC'] ?? null; $businessfact_contact_name = $businessfact['ContactName'] ?? null; $businessfact_contact_title = $businessfact['Title'] ?? null; $businessfact_license_date = $businessfact['LicenseDate'] ?? null; $businessfact_home_based_business = $businessfact['Home'] ?? null; $businessfact_year_established = $businessfact['YearEst'] ?? null; $businessfact_entry_date = $businessfact['EntryDate'] ?? null; $businessfact_square_feet = $businessfact['SQUARE_FEET'] ?? null; $businessfact_source_lease_ending = $businessfact['SOURCE_LEASE_ENDING'] ?? null; $businessfact_lease_end = $businessfact['TLEASEEND'] ?? null; $statement->bindValue(':businessfact_legacy_id', $businessfact_legacy_id, PDO::PARAM_INT); $statement->bindValue(':businessfact_company', $businessfact_company, PDO::PARAM_STR); $statement->bindValue(':businessfact_address_one', $businessfact_address_one, PDO::PARAM_STR); $statement->bindValue(':businessfact_address_two', $businessfact_address_two, PDO::PARAM_STR); $statement->bindValue(':businessfact_city', $businessfact_city, PDO::PARAM_INT); $statement->bindValue(':businessfact_state', $businessfact_state, PDO::PARAM_STR); $statement->bindValue(':businessfact_zip', $businessfact_zip, PDO::PARAM_STR); $statement->bindValue(':businessfact_county', $businessfact_county, PDO::PARAM_INT); $statement->bindValue(':businessfact_phone', $businessfact_phone, PDO::PARAM_STR); $statement->bindValue(':businessfact_fax', $businessfact_fax, PDO::PARAM_STR); $statement->bindValue(':businessfact_email_address', $businessfact_email_address, PDO::PARAM_STR); $statement->bindValue(':businessfact_business_class', $businessfact_business_class, PDO::PARAM_INT); $statement->bindValue(':businessfact_action', $businessfact_action, PDO::PARAM_INT); $statement->bindValue(':businessfact_employee_count', $businessfact_employee_count, PDO::PARAM_STR); $statement->bindValue(':businessfact_inception', $businessfact_inception, PDO::PARAM_STR); $statement->bindValue(':businessfact_sic_code', $businessfact_sic_code, PDO::PARAM_STR); $statement->bindValue(':businessfact_contact_name', $businessfact_contact_name, PDO::PARAM_STR); $statement->bindValue(':businessfact_contact_title', $businessfact_contact_title, PDO::PARAM_STR); $statement->bindValue(':businessfact_license_date', $businessfact_license_date, PDO::PARAM_STR); $statement->bindValue(':businessfact_home_based_business', $businessfact_home_based_business, PDO::PARAM_STR); $statement->bindValue(':businessfact_year_established', $businessfact_year_established, PDO::PARAM_STR); $statement->bindValue(':businessfact_entry_date', $businessfact_entry_date, PDO::PARAM_STR); $statement->bindValue(':businessfact_square_feet', $businessfact_square_feet, PDO::PARAM_STR); $statement->bindValue(':businessfact_source_lease_ending', $businessfact_source_lease_ending, PDO::PARAM_STR); $statement->bindValue(':businessfact_lease_end', $businessfact_lease_end, PDO::PARAM_STR); $statement->bindValue(':businessfact_creator', 'Migration', PDO::PARAM_STR); $statement->bindValue(':businessfact_created', date("Y-m-d"), PDO::PARAM_STR); $statement->execute(); $counter++; } $this->log("Processed $counter Business Fact records... "); $this->log("Migration of Business Facts Completed"); } // ============================= // Migrate Planning/Zoning Facts // ============================= private function migratePZFacts(): void { $this->log("Migrating Business Facts from decweb"); $this->realestatedatainc_connection->exec("TRUNCATE TABLE pzfacts"); $pzfacts = $this->getPZFacts(); $SQL = "INSERT INTO pzfacts ( pzfact_legacy_id, pzfact_entry_date, pzfact_county, pzfact_professional_name, pzfact_professional_address, pzfact_professional_state, pzfact_professional_phone, pzfact_professional_fax, pzfact_owner_name, pzfact_owner_address, pzfact_owner_state, pzfact_owner_phone, pzfact_owner_fax, pzfact_project_name, pzfact_project_type, pzfact_project_description, pzfact_project_address, pzfact_project_city, pzfact_action_code, pzfact_district, pzfact_land_lot, pzfact_lot, pzfact_block, pzfact_parcel_number, pzfact_action_date, pzfact_acres, pzfact_status, pzfact_dollar_value, pzfact_estimated_ground_break, pzfact_in_city_limit, pzfact_creator, pzfact_created ) VALUES ( :pzfact_legacy_id, :pzfact_entry_date, :pzfact_county, :pzfact_professional_name, :pzfact_professional_address, :pzfact_professional_state, :pzfact_professional_phone, :pzfact_professional_fax, :pzfact_owner_name, :pzfact_owner_address, :pzfact_owner_state, :pzfact_owner_phone, :pzfact_owner_fax, :pzfact_project_name, :pzfact_project_type, :pzfact_project_description, :pzfact_project_address, :pzfact_project_city, :pzfact_action_code, :pzfact_district, :pzfact_land_lot, :pzfact_lot, :pzfact_block, :pzfact_parcel_number, :pzfact_action_date, :pzfact_acres, :pzfact_status, :pzfact_dollar_value, :pzfact_estimated_ground_break, :pzfact_in_city_limit, :pzfact_creator, :pzfact_created )"; $statement = $this->realestatedatainc_connection->prepare($SQL); $counter = 0; foreach ($pzfacts as $pzfact) { $pzfact_legacy_id = $pzfact['PZ_ID'] ?? null; $pzfact_entry_date = $pzfact['EntryDate'] ?? null; $pzfact_county = $this->getCountyByCountyName($pzfact['County']) ?? null; /* Professional */ $pzfact_professional_name = $pzfact['ProfessionalName'] ?? null; $pzfact_professional_address = $pzfact['ProfessionalAddress'] ?? null; $pzfact_professional_state = $pzfact['ProfessionalState'] ?? null; $pzfact_professional_phone = $this->normailizePhoneNumber($pzfact['ProfessionalPhone']) ?? null; $pzfact_professional_fax = $this->normailizePhoneNumber($pzfact['ProfessionalFax']) ?? null; /* Owner */ $pzfact_owner_name = $pzfact['OwnerName'] ?? null; $pzfact_owner_address = $pzfact['OwnerAddress'] ?? null; $pzfact_owner_state = $pzfact['OwnerState'] ?? null; $pzfact_owner_phone = $this->normailizePhoneNumber($pzfact['OwnerPhone']) ?? null; $pzfact_owner_fax = $this->normailizePhoneNumber($pzfact['OwnerFax']) ?? null; /* Project */ $pzfact_project_name = $pzfact['ProjectName'] ?? null; $pzfact_project_type = $this->getDropdownSerialByValue($pzfact['ProjectType'], 3) ?? null; $pzfact_project_description = $pzfact['ProjectDescription'] ?? null; $pzfact_project_address = $pzfact['ProjectAddress'] ?? null; $pzfact_project_city = $this->getCityByCityName($pzfact['ProjectCity']) ?? null; /* Zoning / action */ $pzfact_action_code = $this->getDropdownSerialByValue($pzfact['ActionCode'], 8) ?? null; $pzfact_district = $pzfact['District'] ?? null; /* Parcel */ $pzfact_land_lot = $pzfact['LL'] ?? null; $pzfact_lot = $pzfact['Lot'] ?? null; $pzfact_block = $pzfact['Block'] ?? null; $pzfact_parcel_number = $pzfact['Parcel'] ?? null; /* Dates / values */ $pzfact_action_date = $pzfact['ActionDate'] ?? null; $pzfact_acres = $pzfact['Acres'] ?? null; $pzfact_status = $pzfact['Status'] ?? null; $pzfact_dollar_value = $pzfact['DollarValueID'] ?? null; $pzfact_estimated_ground_break = $pzfact['Est_Ground_Breaking'] ?? null; /* Flags */ $pzfact_in_city_limit = isset($pzfact['City']) ? (int) $pzfact['City'] : 0; /* Audit */ $pzfact_creator = 'Migration'; $pzfact_created = date('Y-m-d H:i:s'); $statement->bindValue(':pzfact_legacy_id', $pzfact_legacy_id, PDO::PARAM_INT); $statement->bindValue(':pzfact_entry_date', $pzfact_entry_date, PDO::PARAM_STR); $statement->bindValue(':pzfact_county', $pzfact_county, PDO::PARAM_INT); $statement->bindValue(':pzfact_professional_name', $pzfact_professional_name, PDO::PARAM_STR); $statement->bindValue(':pzfact_professional_address', $pzfact_professional_address, PDO::PARAM_STR); $statement->bindValue(':pzfact_professional_state', $pzfact_professional_state, PDO::PARAM_STR); $statement->bindValue(':pzfact_professional_phone', $pzfact_professional_phone, PDO::PARAM_STR); $statement->bindValue(':pzfact_professional_fax', $pzfact_professional_fax, PDO::PARAM_STR); $statement->bindValue(':pzfact_owner_name', $pzfact_owner_name, PDO::PARAM_STR); $statement->bindValue(':pzfact_owner_address', $pzfact_owner_address, PDO::PARAM_STR); $statement->bindValue(':pzfact_owner_state', $pzfact_owner_state, PDO::PARAM_STR); $statement->bindValue(':pzfact_owner_phone', $pzfact_owner_phone, PDO::PARAM_STR); $statement->bindValue(':pzfact_owner_fax', $pzfact_owner_fax, PDO::PARAM_STR); $statement->bindValue(':pzfact_project_name', $pzfact_project_name, PDO::PARAM_STR); $statement->bindValue(':pzfact_project_type', $pzfact_project_type, PDO::PARAM_INT); $statement->bindValue(':pzfact_project_description', $pzfact_project_description, PDO::PARAM_STR); $statement->bindValue(':pzfact_project_address', $pzfact_project_address, PDO::PARAM_STR); $statement->bindValue(':pzfact_project_city', $pzfact_project_city, PDO::PARAM_INT); $statement->bindValue(':pzfact_action_code', $pzfact_action_code, PDO::PARAM_INT); $statement->bindValue(':pzfact_district', $pzfact_district, PDO::PARAM_INT); $statement->bindValue(':pzfact_land_lot', $pzfact_land_lot, PDO::PARAM_STR); $statement->bindValue(':pzfact_lot', $pzfact_lot, PDO::PARAM_STR); $statement->bindValue(':pzfact_block', $pzfact_block, PDO::PARAM_STR); $statement->bindValue(':pzfact_parcel_number', $pzfact_parcel_number, PDO::PARAM_STR); $statement->bindValue(':pzfact_action_date', $pzfact_action_date, PDO::PARAM_STR); $statement->bindValue(':pzfact_acres', $pzfact_acres, PDO::PARAM_STR); $statement->bindValue(':pzfact_status', $pzfact_status, PDO::PARAM_STR); $statement->bindValue(':pzfact_dollar_value', $pzfact_dollar_value, PDO::PARAM_STR); $statement->bindValue(':pzfact_estimated_ground_break', $pzfact_estimated_ground_break, PDO::PARAM_STR); $statement->bindValue(':pzfact_in_city_limit', $pzfact_in_city_limit, PDO::PARAM_INT); $statement->bindValue(':pzfact_creator', $pzfact_creator, PDO::PARAM_STR); $statement->bindValue(':pzfact_created', $pzfact_created, PDO::PARAM_STR); $statement->execute(); $counter++; } $this->log("Processed $counter Planning/Zoning Fact records... "); $this->log("Migration of Planning/Zoning Facts Completed"); } // ========================== // Migrate Purchased Services // ========================== private function migratePurchasedServices(): void { $this->log("Migrating Purchased Services from decweb"); // $this->realestatedatainc_connection->exec("TRUNCATE TABLE usersubscriptions"); $this->realestatedatainc_connection->exec("delete from usersubscriptions where usersubscription_user != 1"); $purchasedservices = $this->getPurchasedServices(); $SQL = "INSERT INTO usersubscriptions ( usersubscription_user, usersubscription_subscription, usersubscription_start_date, usersubscription_end_date, usersubscription_creator, usersubscription_created ) VALUES ( :usersubscription_user, :usersubscription_subscription, :usersubscription_start_date, :usersubscription_end_date, :usersubscription_creator, :usersubscription_created )"; $statement = $this->realestatedatainc_connection->prepare($SQL); $counter = 0; foreach ($purchasedservices as $service) { $legacy_user_id = $service['UserName'] ?? null; $legacy_subscription_name = preg_replace('/[^\w]/', '', $service['RptDescript']); $legacy_service_level = $service['ServiceLevel'] ?? null; $subscription_map = [ 'PermitFacts' => [ 'Advanced' => 1, ], 'PermitFactsColumbus' => [ 'Advanced' => 2, ], 'PermitFactsAugusta' => [ 'Advanced' => 3, ], 'PermitFactsMacon' => [ 'Advanced' => 4, ], 'PlanningZoning' => [ 'Basic' => 12, 'Advanced' => 5, // 'Advanced' => 6, ], 'BusinessFacts' => [ 'Advanced' => 7, 'Basic' => 11, ], ]; $subscription_serial = $subscription_map[$legacy_subscription_name][$legacy_service_level] ?? null; $usersubscription_user = $this->getUserByUserId($legacy_user_id) ?? null; $usersubscription_subscription = $subscription_serial; $usersubscription_start_date = date("Y-m-d", strtotime($service['StartDate'])); $usersubscription_end_date = date("Y-m-d", strtotime($service['StopDate'])); $usersubscription_creator = 'Migration'; $usersubscription_created = date('Y-m-d H:i:s'); $statement->bindValue(':usersubscription_user', $usersubscription_user, PDO::PARAM_INT); $statement->bindValue(':usersubscription_subscription', $usersubscription_subscription, PDO::PARAM_INT); $statement->bindValue(':usersubscription_start_date', $usersubscription_start_date, PDO::PARAM_STR); $statement->bindValue(':usersubscription_end_date', $usersubscription_end_date, PDO::PARAM_STR); $statement->bindValue(':usersubscription_creator', $usersubscription_creator, PDO::PARAM_STR); $statement->bindValue(':usersubscription_created', $usersubscription_created, PDO::PARAM_STR); $statement->execute(); $counter++; } $this->log("Processed $counter Purchased Services records... "); $this->log("Migration of Purchased Services Completed"); } // ============================ // Return a Permit Facts Object // ============================ private function getPermitFacts() { $SQL = "select * from PermitFacts_tbl"; $permitfacts = $this->decweb_connection->query($SQL)->fetchAll(PDO::FETCH_ASSOC); return $permitfacts; } // ============================== // Return a Business Facts Object // ============================== private function getBusinessFacts() { $SQL = "select * from BizFacts_tbl"; $businessfacts = $this->decweb_connection->query($SQL)->fetchAll(PDO::FETCH_ASSOC); return $businessfacts; } // ======================== // Return a PZ Facts Object // ======================== private function getPZFacts() { $SQL = "select * from PZ_tbl"; $pzfacts = $this->decweb_connection->query($SQL)->fetchAll(PDO::FETCH_ASSOC); return $pzfacts; } // ======================== // Return a PZ Facts Object // ======================== private function getPurchasedServices() { $SQL = "select * from PurchasedServices_tbl"; $pzfacts = $this->decweb_connection->query($SQL)->fetchAll(PDO::FETCH_ASSOC); return $pzfacts; } // ====================== // Normalize Phone Number // ====================== private function normailizePhoneNumber($phonenumber) { return preg_replace('/\D+/', '', $phonenumber); // \D = non-digit } // ======================= // Normalize Email Address // ======================= function normailizeEmailAddress($email) { // Split at the first # $clean = explode('#', $email, 2)[0]; // Normalize case return strtolower(trim($clean)); } // ============================ // Return a Subscription Object // ============================ private function getUserByUserId($userid) { $SQL = "SELECT user_serial FROM users WHERE user_id = :userid LIMIT 1"; $statement = $this->realestatedatainc_connection->prepare($SQL); $statement->bindValue(':userid', $userid, PDO::PARAM_STR); $statement->execute(); $user = $statement->fetch(PDO::FETCH_ASSOC); return $user['user_serial'] ?? null; } // ======================== // Return a Dropdown Object // ======================== private function getDropdownSerialByValue($description, $dropdown_type) { $SQL = "SELECT dropdown_serial FROM dropdowns WHERE dropdown_value = :description and dropdown_dropdowntype = :dropdown_type LIMIT 1"; $statement = $this->realestatedatainc_connection->prepare($SQL); $statement->bindValue(':description', $description, PDO::PARAM_STR); $statement->bindValue(':dropdown_type', $dropdown_type, PDO::PARAM_INT); $statement->execute(); return $statement->fetchColumn() ?: null; } // ====================== // Return a County Object // ====================== private function getCountyByCountyName($county_name) { $SQL = "SELECT county_serial FROM counties WHERE county_name = :county_name LIMIT 1"; $statement = $this->realestatedatainc_connection->prepare($SQL); $statement->bindValue(':county_name', $county_name, PDO::PARAM_STR); $statement->execute(); return $statement->fetchColumn() ?: null; } // ==================== // Return a City Object // ==================== private function getCityByCityName($city_name) { $SQL = "SELECT city_serial FROM cities WHERE city_name = :city_name LIMIT 1"; $statement = $this->realestatedatainc_connection->prepare($SQL); $statement->bindValue(':city_name', $city_name, PDO::PARAM_STR); $statement->execute(); return $statement->fetchColumn() ?: null; } } ?>