diff --git a/classes/Consolidation.php b/classes/Consolidation.php index c7d3112..dfdd7a6 100644 --- a/classes/Consolidation.php +++ b/classes/Consolidation.php @@ -2,12 +2,12 @@ class Consolidation extends Base { - // Variables. + // Variables private $current_user_serial = 0; - private $current_user_id = ""; - private $current_user_name = ""; - private $current_user_email = ""; + private $current_user_id = ""; + private $current_user_name = ""; + private $current_user_email = ""; // ================= // Class Constructor @@ -20,9 +20,9 @@ class Consolidation extends Base { date_default_timezone_set(self::TIMEZONE); $this->current_user_serial = $_SESSION[self::USER_SERIAL] ?? 0; - $this->current_user_id = $_SESSION[self::USER_ID] ?? ""; - $this->current_user_name = $_SESSION[self::USER_NAME] ?? ""; - $this->current_user_email = $_SESSION[self::USER_EMAIL] ?? ""; + $this->current_user_id = $_SESSION[self::USER_ID] ?? ""; + $this->current_user_name = $_SESSION[self::USER_NAME] ?? ""; + $this->current_user_email = $_SESSION[self::USER_EMAIL] ?? ""; } // ====================== @@ -47,6 +47,11 @@ class Consolidation extends Base { case "run": + $XSLParms = array(); + $permit_record_count = 0; + $processed_record_count = 0; + $connection = null; + $sql_insert = "insert into rdiconsolidations ( rdiconsolidation_permit_facts_id, @@ -85,150 +90,73 @@ class Consolidation extends Base { rdiconsolidation_ytd_value, rdiconsolidation_12mth_prev_permits, rdiconsolidation_date ) - - values ( :rdiconsolidation_permit_facts_id, - :rdiconsolidation_entry_date_str, - :rdiconsolidation_entry_date, - :rdiconsolidation_permit_number, - :rdiconsolidation_permit_date_str, - :rdiconsolidation_permit_date, - :rdiconsolidation_project_addr, - :rdiconsolidation_sub_div_name, - :rdiconsolidation_project_type, - :rdiconsolidation_company, - :rdiconsolidation_address, - :rdiconsolidation_city, - :rdiconsolidation_state, - :rdiconsolidation_zip, - :rdiconsolidation_phone, - :rdiconsolidation_project_city, - :rdiconsolidation_project_state, - :rdiconsolidation_size, - :rdiconsolidation_units, - :rdiconsolidation_building_type, - :rdiconsolidation_value, - :rdiconsolidation_work_type, - :rdiconsolidation_county, - :rdiconsolidation_lot, - :rdiconsolidation_dist_ll, - :rdiconsolidation_ct1, - :rdiconsolidation_owner_name, - :rdiconsolidation_owner_address, - :rdiconsolidation_owner_city, - :rdiconsolidation_owner_state, - :rdiconsolidation_owner_zip, - :rdiconsolidation_owner_phone, - :rdiconsolidation_ytd_permits, - :rdiconsolidation_ytd_value, - :rdiconsolidation_12mth_prev_permits, - :rdiconsolidation_date ) "; - $connection = $this->connect(); + select coalesce(permit_permit_facts_id, 0), + coalesce(permit_entry_date_str, ''), + coalesce(permit_entry_date, '0000-00-00'), + coalesce(permit_permit_number, ''), + coalesce(permit_permit_date_str, ''), + coalesce(permit_permit_date, '0000-00-00'), + coalesce(permit_project_addr, ''), + coalesce(permit_sub_div_name, ''), + coalesce(permit_project_type, ''), + coalesce(permit_company, ''), + coalesce(permit_address, ''), + coalesce(permit_city, ''), + coalesce(permit_state, ''), + coalesce(permit_zip, ''), + coalesce(permit_phone, ''), + coalesce(permit_project_city, ''), + coalesce(permit_project_state, ''), + coalesce(permit_size, 0), + coalesce(permit_units, 0), + coalesce(permit_building_type, 0), + coalesce(permit_value, 0), + coalesce(permit_work_type, 0), + coalesce(permit_county, ''), + coalesce(permit_lot, ''), + coalesce(permit_dist_ll, ''), + coalesce(permit_ct1, 0), + coalesce(permit_owner_name, ''), + coalesce(permit_owner_address, ''), + coalesce(permit_owner_city, ''), + coalesce(permit_owner_state, ''), + coalesce(permit_owner_zip, ''), + coalesce(permit_owner_phone, ''), + coalesce(permit_ytd_permits, 0), + coalesce(permit_ytd_value, 0), + coalesce(permit_12mth_prev_permits, 0), + current_date() + from permits"; - $connection->beginTransaction(); - $statement = $connection->prepare($sql_insert); + try { - $rdi_records = $this->getRdiTable(); - $rdi_record_count = $rdi_records->count(); + $connection = $this->connect(); - $processed_record_count = 0; + $connection->beginTransaction(); - foreach ($rdi_records as $record) { + $permit_record_count = (integer) $connection->query("select count(*) from permits")->fetchColumn(); + $processed_record_count = (integer) $connection->exec($sql_insert); - $rdiconsolidation_permit_facts_id = (integer) $record->permit_facts_id ?: 0; - $rdiconsolidation_entry_date_str = (integer) $record->entry_date_str ?: ""; - $rdiconsolidation_entry_date = (string) $record->entry_date ?: "0000-00-00"; - $rdiconsolidation_permit_number = (string) $record->permit_number ?: ""; - $rdiconsolidation_permit_date_str = (string) $record->permit_date_str ?: ""; - $rdiconsolidation_permit_date = (string) $record->permit_date ?: "0000-00-00"; - $rdiconsolidation_project_addr = (string) $record->project_addr ?: ""; - $rdiconsolidation_sub_div_name = (string) $record->sub_div_name ?: ""; - $rdiconsolidation_project_type = (string) $record->project_type ?: ""; - $rdiconsolidation_company = (string) $record->company ?: ""; - $rdiconsolidation_address = (string) $record->address ?: ""; - $rdiconsolidation_city = (string) $record->city ?: ""; - $rdiconsolidation_state = (string) $record->state ?: ""; - $rdiconsolidation_zip = (string) $record->zip ?: ""; - $rdiconsolidation_phone = (string) $record->phone ?: ""; - $rdiconsolidation_project_city = (string) $record->project_city ?: ""; - $rdiconsolidation_project_state = (string) $record->project_state ?: ""; - $rdiconsolidation_size = (integer) $record->size ?: 0; - $rdiconsolidation_units = (integer) $record->units ?: 0; - $rdiconsolidation_building_type = (integer) $record->building_type ?: 0; - $rdiconsolidation_value = (integer) $record->value ?: 0; - $rdiconsolidation_work_type = (integer) $record->work_type ?: 0; - $rdiconsolidation_county = (string) $record->county ?: ""; - $rdiconsolidation_lot = (string) $record->lot ?: ""; - $rdiconsolidation_dist_ll = (string) $record->dist_ll ?: ""; - $rdiconsolidation_ct1 = (integer) $record->ct1 ?: 0; - $rdiconsolidation_owner_name = (string) $record->owner_name ?: ""; - $rdiconsolidation_owner_address = (string) $record->owner_address ?: ""; - $rdiconsolidation_owner_city = (string) $record->owner_city ?: ""; - $rdiconsolidation_owner_state = (string) $record->owner_state ?: ""; - $rdiconsolidation_owner_zip = (string) $record->owner_zip ?: ""; - $rdiconsolidation_owner_phone = (string) $record->owner_phone ?: ""; - $rdiconsolidation_ytd_permits = (integer) $record->ytd_permits ?: 0; - $rdiconsolidation_ytd_value = (integer) $record->ytd_value ?: 0; - $rdiconsolidation_12mth_prev_permits = (integer) $record->{"12mth_prev_permits"} ?: 0; - $rdiconsolidation_date = date("Y-m-d"); - - $statement->bindValue(":rdiconsolidation_permit_facts_id", $rdiconsolidation_permit_facts_id); - $statement->bindValue(":rdiconsolidation_entry_date_str", $rdiconsolidation_entry_date_str); - $statement->bindValue(":rdiconsolidation_entry_date", $rdiconsolidation_entry_date); - $statement->bindValue(":rdiconsolidation_permit_number", $rdiconsolidation_permit_number); - $statement->bindValue(":rdiconsolidation_permit_date_str", $rdiconsolidation_permit_date_str); - $statement->bindValue(":rdiconsolidation_permit_date", $rdiconsolidation_permit_date); - $statement->bindValue(":rdiconsolidation_project_addr", $rdiconsolidation_project_addr); - $statement->bindValue(":rdiconsolidation_sub_div_name", $rdiconsolidation_sub_div_name); - $statement->bindValue(":rdiconsolidation_project_type", $rdiconsolidation_project_type); - $statement->bindValue(":rdiconsolidation_company", $rdiconsolidation_company); - $statement->bindValue(":rdiconsolidation_address", $rdiconsolidation_address); - $statement->bindValue(":rdiconsolidation_city", $rdiconsolidation_city); - $statement->bindValue(":rdiconsolidation_state", $rdiconsolidation_state); - $statement->bindValue(":rdiconsolidation_zip", $rdiconsolidation_zip); - $statement->bindValue(":rdiconsolidation_phone", $rdiconsolidation_phone); - $statement->bindValue(":rdiconsolidation_project_city", $rdiconsolidation_project_city); - $statement->bindValue(":rdiconsolidation_project_state", $rdiconsolidation_project_state); - $statement->bindValue(":rdiconsolidation_size", $rdiconsolidation_size); - $statement->bindValue(":rdiconsolidation_units", $rdiconsolidation_units); - $statement->bindValue(":rdiconsolidation_building_type", $rdiconsolidation_building_type); - $statement->bindValue(":rdiconsolidation_value", $rdiconsolidation_value); - $statement->bindValue(":rdiconsolidation_work_type", $rdiconsolidation_work_type); - $statement->bindValue(":rdiconsolidation_county", $rdiconsolidation_county); - $statement->bindValue(":rdiconsolidation_lot", $rdiconsolidation_lot); - $statement->bindValue(":rdiconsolidation_dist_ll", $rdiconsolidation_dist_ll); - $statement->bindValue(":rdiconsolidation_ct1", $rdiconsolidation_ct1); - $statement->bindValue(":rdiconsolidation_owner_name", $rdiconsolidation_owner_name); - $statement->bindValue(":rdiconsolidation_owner_address", $rdiconsolidation_owner_address); - $statement->bindValue(":rdiconsolidation_owner_city", $rdiconsolidation_owner_city); - $statement->bindValue(":rdiconsolidation_owner_state", $rdiconsolidation_owner_state); - $statement->bindValue(":rdiconsolidation_owner_zip", $rdiconsolidation_owner_zip); - $statement->bindValue(":rdiconsolidation_owner_phone", $rdiconsolidation_owner_phone); - $statement->bindValue(":rdiconsolidation_ytd_permits", $rdiconsolidation_ytd_permits); - $statement->bindValue(":rdiconsolidation_ytd_value", $rdiconsolidation_ytd_value); - $statement->bindValue(":rdiconsolidation_12mth_prev_permits", $rdiconsolidation_12mth_prev_permits); - $statement->bindValue(":rdiconsolidation_date", $rdiconsolidation_date); - - if (!$statement->execute()) { - - $connection->rollBack(); - $this->debug($connection->errorInfo()[2]); + if ($processed_record_count !== $permit_record_count) { + throw new RuntimeException("Record counts did not match. Rolling back database. Contact Support."); } - $processed_record_count++; - } - - if ($processed_record_count !== $rdi_record_count) { - $connection->rollBack(); - $XSLParms["CONSOLIDATION_ERROR"] = "Record Counts Did not match. Rolling Back Database. Contact Support."; - } else { $connection->commit(); - $this->executeSQL("truncate rdis"); + $connection->exec("truncate permits"); + } catch (Exception $e) { + + if (($connection instanceof PDO) && $connection->inTransaction()) { + $connection->rollBack(); + } + + $XSLParms["CONSOLIDATION-ERROR"] = $e->getMessage(); } - $XSLParms["RDI-RECORD-COUNT"] = $rdi_record_count; + $XSLParms["RDI-RECORD-COUNT"] = $permit_record_count; $XSLParms["PROCESSED-RECORD-COUNT"] = $processed_record_count; - $content = $this->applyXSL("", $this->getXSL("consolidationResults"), $XSLParms); + $content = $this->applyXSL("", $this->getXSL("consolidationResults"), $XSLParms); + $this->displayContent($content); break; @@ -239,16 +167,6 @@ class Consolidation extends Base { } } - // ==================== - // Return an RDI Object - // ==================== - - public function getRdiTable() { - - $SQL = "SELECT * FROM permits"; - - return $this->getTable("rdis", $SQL); - } } ?> diff --git a/classes/Import.php b/classes/Import.php index 49d5f32..5e4539a 100644 --- a/classes/Import.php +++ b/classes/Import.php @@ -45,7 +45,11 @@ class Import extends Base { case "prompt": - $content = $this->applyXSL("", $this->getXSL("uploadCSVFile")); + $import_type = filter_input(INPUT_POST, "import_type") ?: "incremental"; + + $XSLParms['IMPORT-TYPE'] = $import_type; + + $content = $this->applyXSL("", $this->getXSL("uploadCSVFile"), $XSLParms); $this->displayContent($content); break; @@ -54,6 +58,7 @@ class Import extends Base { $XSLParms['UPLOAD_STATUS'] = "UPLOAD SUCCESSFUL"; $environment = (string) $this->getSettings()->Environment ?: "Production"; + $upload_type = filter_input(INPUT_POST, 'upload_type') ?: "incremental"; if (empty($environment)) { $this->logError("Invalid Environment ({$environment}). " . __METHOD__); @@ -63,30 +68,57 @@ class Import extends Base { // Process the Attachment - if (!empty($_FILES)) { + if (empty($_FILES["csvfile"])) { + $this->displayNotice("No CSV file was uploaded."); + return; + } - if (!is_dir($environment_directory)) { - mkdir($environment_directory); - } + if (!is_dir($environment_directory) && !mkdir($environment_directory, 0775, true)) { + $this->displayNotice("Unable to create the upload directory. Please contact Support."); + return; + } - $tempname = $_FILES["csvfile"]["tmp_name"]; - $filename = $_FILES["csvfile"]["name"]; + if ($_FILES["csvfile"]["error"] !== UPLOAD_ERR_OK) { + $this->displayNotice("The CSV file upload failed. Please try again."); + return; + } - if (empty($tempname) or empty($filename)) { - return; - } + $tempname = $_FILES["csvfile"]["tmp_name"]; + $filename = basename($_FILES["csvfile"]["name"]); - if (!move_uploaded_file($tempname, "{$environment_directory}/{$filename}")) { - $XSLParms['UPLOAD_STATUS'] = "UPLOAD FAILED"; - } + if (empty($tempname) or empty($filename)) { + $this->displayNotice("The uploaded CSV file could not be read. Please try again."); + return; } $file_directory = "{$environment_directory}/{$filename}"; - $import = $this->parseCSVFile($file_directory); + if (!move_uploaded_file($tempname, $file_directory)) { + $this->displayNotice("Unable to save the uploaded CSV file. Please contact Support."); + return; + } - $content = $this->applyXSL("", $this->getXSL("uploadMessage"), $XSLParms); - $this->displayContent($content); + if ($upload_type === 'incremental') { + $this->parseCSVFile($file_directory); + $content = $this->applyXSL("", $this->getXSL("uploadMessage"), $XSLParms); + $this->displayContent($content); + return; + } + + if ($upload_type === 'full') { + + $successful = $this->createImportRequest($file_directory); + + if (!$successful) { + $this->displayNotice("An error occurred while creating the import request. Please contact Support."); + return; + } + + $this->displayNotice("Your import has been queued. You will receive a notification when it is complete."); + return; + } + + $this->displayNotice("Invalid upload type. Please contact Support."); break; } @@ -143,73 +175,74 @@ class Import extends Base { $County = (new Counties())->getCountiesByName($County)->record->county_serial; $SQL = "INSERT INTO permits ( - permit_permit_facts_id, - permit_entry_date, - permit_permit_number, - permit_permit_date, - permit_project_addr, - permit_sub_div_name, - permit_project_type, - permit_company, - permit_address, - permit_city, - permit_state, - permit_zip, - permit_phone, - permit_project_city, - permit_project_state, - permit_size, - permit_units, - permit_building_type, - permit_value, - permit_work_type, - permit_county, - permit_lot, - permit_dist_ll, - permit_ct1, - permit_owner_name, - permit_owner_address, - permit_owner_city, - permit_owner_state, - permit_owner_zip, - permit_owner_phone, - permit_ytd_permits, - permit_ytd_value, - permit_12mth_prev_permits ) + permit_permit_facts_id, + permit_entry_date, + permit_permit_number, + permit_permit_date, + permit_project_addr, + permit_sub_div_name, + permit_project_type, + permit_company, + permit_address, + permit_city, + permit_state, + permit_zip, + permit_phone, + permit_project_city, + permit_project_state, + permit_size, + permit_units, + permit_building_type, + permit_value, + permit_work_type, + permit_county, + permit_lot, + permit_dist_ll, + permit_ct1, + permit_owner_name, + permit_owner_address, + permit_owner_city, + permit_owner_state, + permit_owner_zip, + permit_owner_phone, + permit_ytd_permits, + permit_ytd_value, + permit_12mth_prev_permits ) + VALUES ( - :permit_permit_facts_id, - :permit_entry_date, - :permit_permit_number, - :permit_permit_date, - :permit_project_addr, - :permit_sub_div_name, - :permit_project_type, - :permit_company, - :permit_address, - :permit_city, - :permit_state, - :permit_zip, - :permit_phone, - :permit_project_city, - :permit_project_state, - :permit_size, - :permit_units, - :permit_building_type, - :permit_value, - :permit_work_type, - :permit_county, - :permit_lot, - :permit_dist_ll, - :permit_ct1, - :permit_owner_name, - :permit_owner_address, - :permit_owner_city, - :permit_owner_state, - :permit_owner_zip, - :permit_owner_phone, - :permit_ytd_permits, - :permit_ytd_value, - :permit_12mth_prev_permits )"; + :permit_permit_facts_id, + :permit_entry_date, + :permit_permit_number, + :permit_permit_date, + :permit_project_addr, + :permit_sub_div_name, + :permit_project_type, + :permit_company, + :permit_address, + :permit_city, + :permit_state, + :permit_zip, + :permit_phone, + :permit_project_city, + :permit_project_state, + :permit_size, + :permit_units, + :permit_building_type, + :permit_value, + :permit_work_type, + :permit_county, + :permit_lot, + :permit_dist_ll, + :permit_ct1, + :permit_owner_name, + :permit_owner_address, + :permit_owner_city, + :permit_owner_state, + :permit_owner_zip, + :permit_owner_phone, + :permit_ytd_permits, + :permit_ytd_value, + :permit_12mth_prev_permits )"; $statement = $connection->prepare($SQL); @@ -255,6 +288,48 @@ class Import extends Base { return true; } + // ===================== + // Create Import Request + // ===================== + + public function createImportRequest($filepath = "") { + + if (!$filepath) { + return false; + } + + $SQL = "insert into importjobs + + ( importjob_file_path, + importjob_notified, + importjob_creator, + importjob_created ) + + VALUES ( :importjob_file_path, + :importjob_notified, + :importjob_creator, + :importjob_created )"; + + $statement = $this->connect()->prepare($SQL); + + $statement->bindValue(":importjob_file_path", "../{$filepath}", PDO::PARAM_STR); + $statement->bindValue(":importjob_notified", false, PDO::PARAM_BOOL); + $statement->bindValue(":importjob_creator", $this->current_user_name, PDO::PARAM_STR); + $statement->bindValue(":importjob_created", $this->getTimestamp()); + + if (!$statement->execute()) { + return false; + } + + $to = $this->getSettings()->DeveloperEmail; + $subject = "CSV Import Jobs Queued"; + $message = "Import initialized successfully. Processing will start momentarily."; + + (new Email())->sendEmail($to, $subject, $message); + + return true; + } + // ======================== // Return an Formatted Date // ======================== diff --git a/classes/ImportLargeCSV.php b/classes/ImportLargeCSV.php new file mode 100644 index 0000000..048840d --- /dev/null +++ b/classes/ImportLargeCSV.php @@ -0,0 +1,255 @@ +current_user_serial = $_SESSION[self::USER_SERIAL] ?? 0; + $this->current_user_id = $_SESSION[self::USER_ID] ?? ""; + $this->current_user_name = $_SESSION[self::USER_NAME] ?? ""; + $this->current_user_email = $_SESSION[self::USER_EMAIL] ?? ""; + + $this->pagination_page = $_SESSION[self::PAGINATION_PAGE] ?? 1; + } + + // ===================== + // Create Import Request + // ===================== + + public function createImportRequest() { + + $subscription_serial = filter_input(INPUT_POST, "subscription_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0; + + $subscription = (new Subscriptions())->getSubscription($subscription_serial); + + if ($subscription->count() === 0) { + $this->logError("Invalid Subscription ({$subscription_serial}): " . __METHOD__); + } + + $import_type = filter_input(INPUT_POST, "import_type") ?: ""; + $report_type = filter_input(INPUT_POST, "report_type") ?: ""; + + $importjob_import_type = str_replace(' ', '', $subscription->record->subscription_full_title) . "$import_type"; + $importjob_file_name = str_replace(' ', '_', $subscription->record->subscription_full_title) . "_all_records.{$report_type}"; +// $importjob_file_name = str_replace('&', 'And', $subscription->record->subscription_full_title) . "_all_records.{$report_type}"; + + $SQL = "insert into importjobs + + ( importjob_subscription, + importjob_import_type, + importjob_file_name, + importjob_file_type, + importjob_creator, + importjob_created ) + + VALUES ( :importjob_subscription, + :importjob_import_type, + :importjob_file_name, + :importjob_file_type, + :importjob_creator, + :importjob_created )"; + + $statement = $this->connect()->prepare($SQL); + + $statement->bindValue(":importjob_subscription", $subscription_serial); + $statement->bindValue(":importjob_import_type", $importjob_import_type); + $statement->bindValue(":importjob_file_name", $importjob_file_name); + $statement->bindValue(":importjob_file_type", strtoupper($report_type)); + $statement->bindValue(":importjob_creator", $this->current_user_serial); + $statement->bindValue(":importjob_created", $this->getTimestamp()); + + $statement->execute(); + + $this->displayNotice("Your import has been queued. You will receive an notification when it is ready."); + } + + // ============ + // User Imports + // ============ + + public function usersImports() { + + $imports = $this->getUsersImports(); + $popovers = (new Popovers())->getPopovers(); + + $this->markImportsAsSeen(); + + $XML = $this->mergeXML($imports, ""); + $XML = $this->mergeXML($popovers, $XML); + + $html = $this->applyXSL($XML, $this->getXSL("viewImports")); + + $this->displayContent($html); + } + + // ========================= + // Mark User Imports as Seen + // ========================= + + public function markImportsAsSeen() { + + $SQL = "update importjobs + set importjob_notified = true + where importjobs.importjob_creator = {$this->current_user_serial}"; + + $statement = $this->connect()->prepare($SQL); + + $statement->execute(); + } + + // ======================= + // Return a Imports Object + // ======================= + + public function getUsersImports() { + + // Find Criteria + + $find_importjob = filter_input(INPUT_POST, "find_importjob") ?: ""; + $find_words = $this->sanitizeString($find_importjob); + + // Show Per Page + $this->pagination_size = filter_input(INPUT_POST, "importjobs_per_page", FILTER_SANITIZE_NUMBER_INT) ?: $this->pagination_size; + + // Record Count + + $connection = $this->connect(); + + $SQL = "select count(*) as count + from view_importjobs + where view_importjobs.importjob_creator = {$this->current_user_serial} + and view_importjobs.subscription_full_title regexp :subscription_full_title"; + + $statement = $connection->prepare($SQL); + + $statement->bindValue(":subscription_full_title", $find_words); + + $statement->execute(); + + $recordset = $statement->fetch(PDO::FETCH_ASSOC); + + $count = (count($recordset) > 0) ? (integer) $recordset["count"] : (integer) 0; + + // Pagination + + $pagination = filter_input(INPUT_POST, "pagination") ?: ""; + + switch ($pagination) { + + case "first" : $this->pagination_page = 1; + break; + case "previous" : $this->pagination_page = max(($this->pagination_page - 1), 1); + break; + case "next" : $this->pagination_page = ($this->pagination_page + 1); + break; + case "last" : $this->pagination_page = ($count / $this->pagination_size); + break; + } + + $offset = (($this->pagination_page * $this->pagination_size) - $this->pagination_size); + $start = ($offset + 1); + $end = min($count, (($start + $this->pagination_size) - 1)); + + // Data + + $SQL = "select view_importjobs.* + from view_importjobs + where view_importjobs.importjob_creator = {$this->current_user_serial} + and view_importjobs.subscription_full_title regexp :subscription_full_title + limit {$this->pagination_size} + offset {$offset}"; + + $statement = $connection->prepare($SQL); + + $statement->bindValue(":subscription_full_title", $find_words); + + $statement->execute(); + + $importjobs = $this->getTableXML("importjobs", $statement); + + // Insert Pagination Attributes + + $importjobs->addAttribute("count", $count); + $importjobs->addAttribute("start", $start); + $importjobs->addAttribute("end", $end); + $importjobs->addAttribute("page", $this->pagination_page); + + $_SESSION[self::PAGINATION_PAGE] = $this->pagination_page; + + return $importjobs; + } + + // ===================================== + // Return if users imports are completed + // ===================================== + + public function checkUsersCompletedImports_AJAX() { + + $SQL = "select count(*) as notify + from view_importjobs + where view_importjobs.importjob_creator = :current_user_serial + and view_importjobs.importjob_status = 'complete' + and view_importjobs.importjob_notified = 0"; + + $importjobs = $this->getTable("importjobs", $SQL, array('current_user_serial' => $this->current_user_serial)); + + // Create the User Object. + + $users_imports = array(); + + $users_imports["notify"] = ($importjobs->record->notify == 0) ? false : true; + + if ($_SESSION[self::AJAX]) { + echo json_encode($users_imports); + } else { + return $users_imports; + } + } + + // =============== + // Download Import + // =============== + + public function downloadImport() { + + $import_code = filter_input(INPUT_POST, "import_code") ?: ""; + + $SQL = "select view_importjobs.* + from view_importjobs + where view_importjobs.importjob_download_token = :import_code + and view_importjobs.importjob_status = 'complete' limit 1"; + + $importjob = $this->getTable("importjob", $SQL, array("import_code" => $import_code)); + + $file_path = str_replace('../imports/', './imports/', $importjob->record->importjob_file_path); + $file_name = $importjob->record->importjob_file_name; + + header('Content-Type: application/octet-stream'); + header("Content-Disposition: attachment; filename=\"$file_name\""); + + readfile($file_path); + } +} + +?> diff --git a/classes/Process_ImportRequest.php b/classes/Process_ImportRequest.php new file mode 100644 index 0000000..53a2096 --- /dev/null +++ b/classes/Process_ImportRequest.php @@ -0,0 +1,584 @@ += $maxLogSize) { + + $rotated = $logfile . '-'; + rename($logfile, $rotated); + } + + $this->log = fopen($logfile, "a"); + + if ($this->log === false) { + + error_log("Process_ImportRequest : Unable to open log file"); + exit(); + } + + $this->log(str_repeat("*", 50)); + + // Get Settings + + $this->settings = new SimpleXMLElement((file_exists($basePath . "/xml/settings.xml") ? file_get_contents($basePath . "/xml/settings.xml") : "")); + + if ($this->settings->count() === 0) { + + error_log("Process_ImportRequest : Unable to initialize settings"); + exit(); + } + + // Open Database Connections + + $this->connection = $this->connect(); + } + + // ================================================ + // Return a New DEC-DR Database Database Connection + // ================================================ + + public function connect() { + + if ($this->settings->Environment == 'Production') { + $host = 'localhost'; + } else { + $host = '192.168.1.190'; + } + + try { + + $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_EMULATE_PREPARES, false); + $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (PDOException | Exception $e) { + + trigger_error($e->getMessage()); + } + + return $connection; + } + + // ============================= + // Log a message to the Log File + // ============================= + + private function log($message = "") { + + fwrite($this->log, date("Y-m-d H:i A") . " : " . "{$message} \n"); + } + + // =================== + // Process Parcel Data + // =================== + + public function process() { + + $this->log("Starting All Record Import Job"); + + $this->runImportJob(); + + $this->purgeImportJobs(); + + $this->log("Completed All Record Import Job"); + } + + // ============== + // Run Import Job + // ============== + + private function runImportJob() { + + $this->log("Processing Import Job"); + + $nextjob = $this->claimNextJob(); + + if (!$nextjob) { + $this->log("Nothing to do."); + return; + } + + $importjob_serial = (int) $nextjob['importjob_serial']; + $importjob_file_path = (string) $nextjob['importjob_file_path']; + + try { + + $success = $this->parseCSVFile($importjob_file_path); + + $SQL = "UPDATE importjobs + SET importjob_status = 'complete', + importjob_completed = NOW() + WHERE importjob_serial = :importjob_serial "; + + $update = $this->connect()->prepare($SQL); + + $update->execute([':importjob_serial' => $importjob_serial]); + } catch (Throwable $e) { + + $SQL = "UPDATE importjobs + SET importjob_status = 'failed', + importjob_completed = NOW(), + importjob_error_message = :importjob_error_message + WHERE importjob_serial = :importjob_serial"; + + $fail = $this->connect()->prepare($SQL); + + $fail->execute([':importjob_serial' => $importjob_serial, ':importjob_error_message' => $e->getMessage()]); + + // Notify Admins of error + $this->sendWebHookAlert("Import Job {$importjob_serial} has failed. Check Error Logs for details."); + $this->email_admins("Import Job Failure.", "Import Job {$importjob_serial} has failed. Check Error Logs for details."); + } + } + + // ================================= + // Purge Imports Older then 24 Hours + // ================================= + + private function purgeImportJobs() { + + $this->log("Purging Import Jobs Database Files"); + + $this->purgeFiles(); + } + + // ======================= + // Claim Next Job in Queue + // ======================= + + private function claimNextJob() { + + $this->connection->beginTransaction(); + + $SQL = "SELECT view_importjobs.* + FROM view_importjobs + WHERE view_importjobs.importjob_status = 'queued' + ORDER BY view_importjobs.importjob_created ASC + LIMIT 1 FOR UPDATE"; + + $statement = $this->connection->prepare($SQL); + + $statement->execute(); + + $job = $statement->fetch(PDO::FETCH_ASSOC); + + if (!$job) { + $this->connection->commit(); + return null; + } + + $SQL = "UPDATE importjobs + SET importjob_status = 'running' + WHERE importjob_serial = :importjob_serial"; + + $statement = $this->connection->prepare($SQL); + + $statement->execute([':importjob_serial' => (int) $job['importjob_serial']]); + + $this->connection->commit(); + + return $job; + } + + // ==================== + // Purges Files on Disk + // ==================== + + private function purgeFiles() { + + $SQL = "select * + from importjobs + where importjobs.importjob_created < (NOW() - INTERVAL 24 HOUR)"; + + $importfiles = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC); + + if (count($importfiles) == 0) { + $this->log("Nothing To Purge"); + return; + } + + foreach ($importfiles as $files) { + + $filename = $files['importjob_file_path']; + + unlink($filename); + } + + $baseDir = './imports'; + + foreach (new DirectoryIterator($baseDir) as $item) { + + if ($item->isDot() || !$item->isDir()) { + continue; + } + + $folderPath = $item->getPathname(); + + // Check if directory is empty + $files = scandir($folderPath); + + if ($files !== false && count($files) === 2) { + rmdir($folderPath); + } + } + + $this->log("Purging Import Jobs Database Records"); + + $SQL = "delete from importjobs where importjobs.importjob_created < (NOW() - INTERVAL 24 HOUR)"; + + $this->connection->query($SQL); + } + + // ======================= + // Format Raw Phone Number + // ======================= + + public function formatPhoneNumber(string $phonenumber): string { + + // Remove everything except digits + $digits = preg_replace('/\D+/', '', $phonenumber); + + // Handle leading country code (US) + if (strlen($digits) === 11 && str_starts_with($digits, '1')) { + $digits = substr($digits, 1); + } + + return substr($digits, 0, 3) . '-' . substr($digits, 3, 3) . '-' . substr($digits, 6, 4); + } + + // ============== + // Parse CSV File + // ============== + + public function parseCSVFile($file) { + + $csvfile = fopen($file, 'r'); + + $RecordCount = 0; + + while (($data = fgetcsv($csvfile, 0, ",", '"', '\\')) !== FALSE) { + + $PermitFactsId = $data[1]; + $EntryDate = $this->fixDate($data[2]); + $PermitNumber = $data[4]; + $PermitDate = $this->fixDate($data[5]); + $ProjectAddress = $data[7]; + $SubdivisionName = $data[8]; + $ProjectType = $data[9]; + $Company = substr($data[10], 0, 30); + $Address = substr($data[11], 0, 30); + $City = $data[12]; + $State = $data[13]; + $Zip = $data[14]; + $Phone = $data[15]; + $ProjectCity = $data[16]; + $ProjectState = $data[17]; + $Size = $data[18] ?: 0; + $Units = $data[19]; + $BuildingType = $data[20]; + $Value = $data[21] ?: 0; + $WorkType = $data[22]; + $County = $data[23] ?: 0; + $Lot = $data[24]; + $DistrictLL = $data[25]; + $Ctl = $data[26]; + $OwnerName = substr($data[27], 0, 30); + $OwnerAddress = substr($data[28], 0, 30); + $OwnerCity = $data[29]; + $OwnerState = substr($data[30], 0, 2); + $OwnerZip = $data[31]; + $OwnerPhone = $data[32]; + $YTDPermits = $data[33] ?: 0; + $YTDValue = $data[34] ?: 0; + $TwelveMonthPreviousPermits = $data[35] ?: 0; + + $County = $this->getCountiesByName($County)['county_serial'] ?? 0; + + $SQL = "INSERT INTO permits ( + permit_permit_facts_id, + permit_entry_date, + permit_permit_number, + permit_permit_date, + permit_project_addr, + permit_sub_div_name, + permit_project_type, + permit_company, + permit_address, + permit_city, + permit_state, + permit_zip, + permit_phone, + permit_project_city, + permit_project_state, + permit_size, + permit_units, + permit_building_type, + permit_value, + permit_work_type, + permit_county, + permit_lot, + permit_dist_ll, + permit_ct1, + permit_owner_name, + permit_owner_address, + permit_owner_city, + permit_owner_state, + permit_owner_zip, + permit_owner_phone, + permit_ytd_permits, + permit_ytd_value, + permit_12mth_prev_permits ) + + VALUES ( + :permit_permit_facts_id, + :permit_entry_date, + :permit_permit_number, + :permit_permit_date, + :permit_project_addr, + :permit_sub_div_name, + :permit_project_type, + :permit_company, + :permit_address, + :permit_city, + :permit_state, + :permit_zip, + :permit_phone, + :permit_project_city, + :permit_project_state, + :permit_size, + :permit_units, + :permit_building_type, + :permit_value, + :permit_work_type, + :permit_county, + :permit_lot, + :permit_dist_ll, + :permit_ct1, + :permit_owner_name, + :permit_owner_address, + :permit_owner_city, + :permit_owner_state, + :permit_owner_zip, + :permit_owner_phone, + :permit_ytd_permits, + :permit_ytd_value, + :permit_12mth_prev_permits )"; + + $statement = $this->connection->prepare($SQL); + + $statement->bindValue(":permit_permit_facts_id", $PermitFactsId); + $statement->bindValue(":permit_permit_number", $PermitNumber); + $statement->bindValue(":permit_entry_date", $EntryDate); + $statement->bindValue(":permit_permit_date", $PermitDate); + $statement->bindValue(":permit_project_addr", $ProjectAddress); + $statement->bindValue(":permit_sub_div_name", $SubdivisionName); + $statement->bindValue(":permit_project_type", $ProjectType); + $statement->bindValue(":permit_company", $Company); + $statement->bindValue(":permit_address", $Address); + $statement->bindValue(":permit_city", $City); + $statement->bindValue(":permit_state", $State); + $statement->bindValue(":permit_zip", $Zip); + $statement->bindValue(":permit_phone", $Phone); + $statement->bindValue(":permit_project_city", $ProjectCity); + $statement->bindValue(":permit_project_state", $ProjectState); + $statement->bindValue(":permit_size", $Size); + $statement->bindValue(":permit_units", $Units); + $statement->bindValue(":permit_building_type", $BuildingType); + $statement->bindValue(":permit_value", $Value); + $statement->bindValue(":permit_work_type", $WorkType); + $statement->bindValue(":permit_county", $County); + $statement->bindValue(":permit_lot", $Lot); + $statement->bindValue(":permit_dist_ll", $DistrictLL); + $statement->bindValue(":permit_ct1", $Ctl); + $statement->bindValue(":permit_owner_name", $OwnerName); + $statement->bindValue(":permit_owner_address", $OwnerAddress); + $statement->bindValue(":permit_owner_city", $OwnerCity); + $statement->bindValue(":permit_owner_state", $OwnerState); + $statement->bindValue(":permit_owner_zip", $OwnerZip); + $statement->bindValue(":permit_owner_phone", $OwnerPhone); + $statement->bindValue(":permit_ytd_permits", $YTDPermits); + $statement->bindValue(":permit_ytd_value", $YTDValue); + $statement->bindValue(":permit_12mth_prev_permits", $TwelveMonthPreviousPermits); + + $statement->execute(); + + $RecordCount++; + } + + return true; + } + + // ======================== + // Return a Counties Object + // ======================== + + public function getCountiesByName($county_name) { + + $SQL = "select view_counties.* + from view_counties + where view_counties.county_name = '{$county_name}'"; + + $statement = $this->connection->prepare($SQL); + + $statement->execute(); + + $county_name = $statement->fetch(PDO::FETCH_ASSOC); + + return $county_name; + } + + // ========================== + // Send an Alert/Notification + // ========================== + + public function sendWebHookAlert($message, $title = "🚨 DEC Application Alert", $alerttype = "1fff00") { + + $webhookUrl = (string) $this->settings->DiscordWebHookURL; + + $data = [ + 'embeds' => [[ + 'title' => $title, + 'description' => $message, + 'color' => hexdec($alerttype), + 'footer' => [ + 'text' => 'Real Estate Data Inc. 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 = (string) $settings->SmtpServer; + $username = (string) $settings->SmtpUserName; + $password = (string) $settings->SmtpPassword; + + // Recipient Variables + $from_email = (string) $settings->FromEmail; + $from_name = (string) $settings->FromName; + + $support_email = (string) $settings->SupportEmail; + $support_name = (string) $settings->SupportName; + + if ((string) $settings->Environment == 'Production') { + + $admin_email = (string) $settings->AdminEmail; + $admin_name = (string) $settings->AdminName; + } + + // Content Variables + + $attachment_name = ""; + + $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: {$e->ErrorInfo}"); + } + } + + // ======================== + // Return an Formatted Date + // ======================== + + public function fixDate($givenDate) { + + $formattedDate = date('Y-m-d', strtotime($givenDate)); + + return $formattedDate; + } +} + +?> \ No newline at end of file diff --git a/classes/Subscriptions.php b/classes/Subscriptions.php index 6a94fcc..0c4570e 100644 --- a/classes/Subscriptions.php +++ b/classes/Subscriptions.php @@ -321,10 +321,12 @@ class Subscriptions extends Base { $user_serial = filter_input(INPUT_POST, "user_reference", FILTER_SANITIZE_NUMBER_INT) ?: 0; $user = (new Users())->getUser($user_serial); + $usersubscriptions = $this->getUsersActiveSubscriptions($user_serial); $subscriptions = $this->getSubscriptions(); $XML = $this->mergeXML($user, ""); $XML = $this->mergeXML($subscriptions, $XML); + $XML = $this->mergeXML($usersubscriptions, $XML); $content = $this->applyXSL($XML, $this->getXSL("addSubscriptionToUser")); @@ -334,11 +336,18 @@ class Subscriptions extends Base { case "add": - $subscription_serial = filter_input(INPUT_POST, "subscription_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0; $user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0; - $subscription_stop_date = filter_input(INPUT_POST, "subscription_stop_date") ?: ""; + $subscription_serials = filter_input(INPUT_POST, "subscriptions", FILTER_SANITIZE_NUMBER_INT, FILTER_REQUIRE_ARRAY) ?: array(); + $subscription_end_dates = filter_input(INPUT_POST, "subscription_end_date", FILTER_DEFAULT, FILTER_REQUIRE_ARRAY) ?: array(); + $subscriptions = array(); - $subscription_stop_date = date("Y-m-d", strtotime($subscription_stop_date)); + foreach ($subscription_serials as $index => $subscription_serial) { + + $subscriptions[] = array( + "usersubscription_subscription" => $subscription_serial, + "usersubscription_end_date" => date("Y-m-d", strtotime($subscription_end_dates[$index] ?? "")) + ); + } $SQL = "insert into usersubscriptions @@ -357,12 +366,19 @@ class Subscriptions extends Base { $statement = $this->connect()->prepare($SQL); $statement->bindValue(":usersubscription_user_serial", $user_serial); - $statement->bindValue(":usersubscription_subscription_serial", $subscription_serial); - $statement->bindValue(":usersubscription_stop_date", $subscription_stop_date); $statement->bindValue(":usersubscription_timestamp", $this->getTimestamp()); $statement->bindValue(":usersubscription_origin", $this->current_user_name); - $statement->execute(); + foreach ($subscriptions as $subscription) { + + $subscription_serial = $subscription['usersubscription_subscription']; + $subscription_stop_date = $subscription['usersubscription_end_date']; + + $statement->bindValue(":usersubscription_subscription_serial", $subscription_serial); + $statement->bindValue(":usersubscription_stop_date", $subscription_stop_date); + + $statement->execute(); + } break; @@ -583,7 +599,7 @@ class Subscriptions extends Base { $XML = $this->mergeXML($subscriptions, ""); $XML = $this->mergeXML($alerts, $XML); $XML = $this->mergeXML($popovers, $XML); - + $content = $this->applyXSL($XML, $this->getXSL("userSubscriptions")); $this->displayContent($content); diff --git a/classes/Users.php b/classes/Users.php index 5adde4a..a703e67 100644 --- a/classes/Users.php +++ b/classes/Users.php @@ -743,6 +743,20 @@ class Users extends Base { error_log($statement->errorInfo()[2]); } } + + // ==================================== + // Return a Users Login Activity Object + // ==================================== + + public function getLoginActivity($userserial) { + + $SQL = "select view_journal.journal_timestamp_verbose, view_journal.journal_ip + from view_journal + where view_journal.journal_origin = '{$userserial}' + and view_journal.journal_entry like '{$userserial} %logged in%'"; + + return $this->getTable("loginactivity", $SQL); + } } ?> diff --git a/js/addSubscriptionToUser.js b/js/addSubscriptionToUser.js index 64b529b..fd1f990 100644 --- a/js/addSubscriptionToUser.js +++ b/js/addSubscriptionToUser.js @@ -16,9 +16,9 @@ $(document).ready(function () { placeholder: "MM/DD/YYYY", showMaskOnHover: false}); - // -------------- - // Select Picker. - // -------------- + // ------------- + // Select Picker + // ------------- $(".search-dropdown").selectpicker({noneSelectedText: "", style: "", @@ -34,25 +34,31 @@ $(document).ready(function () { // Date Pickers // ------------ - $("#subscription_stop_date").datepicker({format: "mm-dd-yyyy", - container: "div.subscription_stop_date", - keyboardNavigation: false, - showOnFocus: false, - autoclose: true, - orientation: "left bottom", - todayBtn: "linked", - todayHighlight: true}); + $(".subscription-end-date").each(function () { + $(this).datepicker({ + format: "mm/dd/yyyy", + container: $(this).closest(".subscription_end_date"), + keyboardNavigation: false, + showOnFocus: false, + autoclose: true, + orientation: "bottom left", + todayBtn: "linked", + todayHighlight: true + }); + }); + // --------------------- // Show Calendar Buttons // --------------------- - $(".btnSubscriptionStopDateCalendar").on("click", function (e) { - + $(".btnSubscriptionEndDateCalendar").on("click", function (e) { e.preventDefault(); + let end_date = $(this).siblings(".subscription-end-date"); - $("#subscription_stop_date").focus().datepicker("show"); - + if (!end_date.prop("disabled")) { + end_date.focus().datepicker("show"); + } }); // ----------- @@ -81,24 +87,81 @@ $(document).ready(function () { }); + function setSubscriptionDateState(subscription_checkbox) { + + let row = subscription_checkbox.closest("tr"); + let checked = subscription_checkbox.prop("checked"); + + row.find(".subscription-end-date").prop("disabled", !checked); + row.find(".btnSubscriptionEndDateCalendar").prop("disabled", !checked); + + } + + function parseSubscriptionDate(date_value) { + + let parts = date_value.split("/"); + + return new Date(parts[2], parts[0] - 1, parts[1]); + + } + + $(".subscription-checkbox").each(function () { + setSubscriptionDateState($(this)); + }); + + $(".subscription-checkbox").on("change", function () { + + let totalSubscriptions = $(".subscription-checkbox").length; + let checkedSubscriptions = $(".subscription-checkbox:checked").length; + + $("#selectAllSubscriptions").prop("checked", totalSubscriptions === checkedSubscriptions); + setSubscriptionDateState($(this)); + + }); + + $("#selectAllSubscriptions").on("change", function () { + + $(".subscription-checkbox").prop("checked", $(this).prop("checked")).each(function () { + setSubscriptionDateState($(this)); + }); + + }); + // --------------- // Validate Inputs // --------------- $("#addSubscriptionToUser").submit(function (e) { - var subscription_title = $("#subscription_serial"), subscription_stop_date = $("#subscription_stop_date"); + let checkedSubscriptions = $(".subscription-checkbox:checked"); clearInputErrors(); + $("#subscriptions_errors").addClass("d-none"); - if ($("option:selected", subscription_title).val() === "") { - createInputError(subscription_title, "Subscription is required"); + // Validate at least one subscription selected + if (checkedSubscriptions.length === 0) { + e.preventDefault(); + $("#subscriptions_errors").removeClass("d-none"); return false; } - if (isBlank(subscription_stop_date)) { - createInputError(subscription_stop_date, "Date is required"); - return false; + for (let i = 0; i < checkedSubscriptions.length; i++) { + + let row = checkedSubscriptions.eq(i).closest("tr"); + let end_date = row.find(".subscription-end-date"); + + if (isBlank(end_date)) { + createInputError(end_date, "Date is required"); + e.preventDefault(); + return false; + } + + if ((!isBlank(end_date)) && (!end_date.inputmask("isComplete"))) { + createInputError(end_date, "Date is incomplete"); + e.preventDefault(); + return false; + } + } e.preventDefault(); diff --git a/js/consolidationResults.js b/js/consolidationResults.js index 37aeac3..89fe18d 100644 --- a/js/consolidationResults.js +++ b/js/consolidationResults.js @@ -15,6 +15,7 @@ $(document).ready(function () { $sessionStorage.action = "Import.uploadCSVFile"; $sessionStorage.step = "prompt"; + $sessionStorage.import_type = "full"; sessionStorage.setItem($application, JSON.stringify($sessionStorage)); diff --git a/js/uploadCSVFile.js b/js/uploadCSVFile.js index 5587c57..18af28d 100644 --- a/js/uploadCSVFile.js +++ b/js/uploadCSVFile.js @@ -54,7 +54,7 @@ $(document).ready(function () { $(".file-input").on("change", function (e) { - if ((e.target.files.length === 0) || (e.target.files[0].type !== "text/csv")) { + if (e.target.files.length === 0) { return false; } @@ -93,7 +93,7 @@ $(document).ready(function () { .done(function () { $sessionStorage.action = ""; - $sessionStorage.toast = "RDI Records Imported"; + $sessionStorage.toast = ($("#upload_type").val() === "full") ? "CSV Import Queued" : "Permit Records Imported"; sessionStorage.setItem($application, JSON.stringify($sessionStorage)); @@ -103,4 +103,4 @@ $(document).ready(function () { }); -}); \ No newline at end of file +}); diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml index 6807a2b..795ebd9 100644 --- a/nbproject/private/private.xml +++ b/nbproject/private/private.xml @@ -2,6 +2,11 @@ - + + file:/C:/Users/JamesRichie/OneDrive%20-%20OneSource%20IT%20Solutions%20LLC/Documents/NetBeansProjects/dec-international/atlhousingreport/classes/Import.php + file:/C:/Users/JamesRichie/OneDrive%20-%20OneSource%20IT%20Solutions%20LLC/Documents/NetBeansProjects/dec-international/atlhousingreport/xsl/uploadCSVFile.xsl + file:/C:/Users/JamesRichie/OneDrive%20-%20OneSource%20IT%20Solutions%20LLC/Documents/NetBeansProjects/dec-international/atlhousingreport/classes/Consolidation.php + file:/C:/Users/JamesRichie/OneDrive%20-%20OneSource%20IT%20Solutions%20LLC/Documents/NetBeansProjects/dec-international/atlhousingreport/xml/settings.xml + diff --git a/reports/Print_UserSummary.php b/reports/Print_UserSummary.php index 8752915..5e79444 100644 --- a/reports/Print_UserSummary.php +++ b/reports/Print_UserSummary.php @@ -81,7 +81,8 @@ class User_Summary extends TCPDF { } $usernotes = (new Notes())->getAllNotes($this->user_serial); - $subscriptions = (new Subscriptions())->getUsersSubscriptions($this->user_serial); + $subscriptions = (new Subscriptions())->getUsersActiveSubscriptions($this->user_serial); + $userloginactivity = (new Users())->getLoginActivity($user->record->user_name); // Set report defaults @@ -167,11 +168,57 @@ class User_Summary extends TCPDF { $this->RoundedRect(10, 23, 190, $this->GetY() - 21, 1.50, "1111"); + // -------------- + // Login Activity + // -------------- + + $this->Ln(7); + + $this->SetFont("Calibri", "B", 12); + $this->Cell(0, 0, "Login Activity", 0, 1, "L", false, "", 3); + $this->Ln(3); + $this->SetFont("Calibri", "B", 10); + + $this->Cell(30, 0, "Date", "B", 0, "L", false, "", 3); + $this->Cell(20, 0, "IP Address", "B", 0, "L", false, "", 3); + + $this->SetFont("Calibri", "", 10); + + $this->Ln(6); + + if ($userloginactivity->count() == 0) { + + $this->Cell(0, 0, "No Login Activity...", 0, 1, "L", false, "", 3); + } else { + + foreach ($userloginactivity as $userlogin) { + + $this->Cell(30, 0, $userlogin->journal_timestamp_verbose, 0, 0, "L", false, "", 3); + $this->Cell(20, 0, $userlogin->journal_ip, 0, 0, "L", false, "", 3); + + $this->Ln(4); + + if ($this->GetY() > 265) { + + $this->AddPage("P", "LETTER"); + + $this->SetFont("Calibri", "B", 10); + + $this->Cell(30, 0, "Date", "B", 0, "L", false, "", 3); + $this->Cell(20, 0, "Origin", "B", 0, "L", false, "", 3); + + $this->SetFont("Calibri", "", 10); + + $this->Ln(6); + } + } + } + // ----- // Notes // ----- - $this->Ln(7); + $this->Ln(5); $this->SetFont("Calibri", "B", 12); $this->Cell(0, 0, "Notes", 0, 1, "L", false, "", 3); @@ -282,7 +329,7 @@ class User_Summary extends TCPDF { $this->setCellPaddings(0, 0, 0, 0); $this->setCellMargins(2, 0, 0, 0); - $this->ImageSVG('images/dec-logo.svg', 10, 0, 25, 25); + $this->Image('images/dec-international-logo.png', 10, 7, 25, 10); $this->SetFont("Calibri", "B", 16); diff --git a/scripts/GHRCronScript.sh b/scripts/GHRCronScript.sh new file mode 100644 index 0000000..17bda05 --- /dev/null +++ b/scripts/GHRCronScript.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# -- Development -- +cd /var/www/html/clients/dec-international/ahrhousingreport +# -- Production -- +#cd /var/www/html/webapps/dec-international/ahrhousingreport +php ./scripts/ImportRequest_Worker.php + diff --git a/scripts/ImportRequest_Worker.php b/scripts/ImportRequest_Worker.php new file mode 100644 index 0000000..efefcda --- /dev/null +++ b/scripts/ImportRequest_Worker.php @@ -0,0 +1,9 @@ +process(); +?> \ No newline at end of file diff --git a/scripts/rdiusers.log b/scripts/rdiusers.log new file mode 100644 index 0000000..fb173ba --- /dev/null +++ b/scripts/rdiusers.log @@ -0,0 +1,10 @@ +2025-11-14 22:35 PM : ************************************************** +2025-11-14 22:35 PM : Starting Migration of RDI Users +2025-11-14 22:35 PM : Processing RDI Users +2025-11-14 22:35 PM : Processed 10 records... +2025-11-14 22:35 PM : Processed 20 records... +2025-11-14 22:35 PM : Processed 30 records... +2025-11-14 22:35 PM : Processed 40 records... +2025-11-14 22:35 PM : Processed 50 records... +2025-11-14 22:35 PM : Processed 60 records... +2025-11-14 22:35 PM : RDI Users Migration Process Complete diff --git a/tables/importjobs.php b/tables/importjobs.php new file mode 100644 index 0000000..996a77e --- /dev/null +++ b/tables/importjobs.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/views/view_importjobs.php b/views/view_importjobs.php new file mode 100644 index 0000000..30a2c89 --- /dev/null +++ b/views/view_importjobs.php @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/xsl/addSubscriptionToUser.xsl b/xsl/addSubscriptionToUser.xsl index e6d8164..1ba4906 100644 --- a/xsl/addSubscriptionToUser.xsl +++ b/xsl/addSubscriptionToUser.xsl @@ -22,48 +22,88 @@ | + | + +
- - + + + - - -
+
+ + The subscriptions listed below will be added to the user. +
+ +
+ + +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + +
+ + SubscriptionEnd Date
+ This user already has all available subscriptions. +
+ +
+ +
+
+ + Select at least one subscription to copy. +
+
+ +
-
- - -
- -
- - -
- -
-
- -
- - +
+ +
+ + + +
@@ -74,22 +114,24 @@ - - - - - - - - - - - - + + + + + + + + + + +
+ + +
+ +
- + diff --git a/xsl/addSubscriptionToUser_orig.xsl b/xsl/addSubscriptionToUser_orig.xsl new file mode 100644 index 0000000..e6d8164 --- /dev/null +++ b/xsl/addSubscriptionToUser_orig.xsl @@ -0,0 +1,95 @@ + + + + + + + + 1.0 + Add Subscription to User + addSubscriptionToUser + + + +