Welcome to the Prospectus Builder

MESSAGE; } public static function getEndUserSubject() { return <<json_data = CacheHandler::CacheUpToDate($json); // if the latest cache is not the latest version of the JSON if($this->json_data == false) { // read the original JSON file $this->json_data = $this->returnStructure($json); // Write the new structure to the Cache CacheHandler::WriteNewerCache($this->json_data); } } function returnStructure($json) { //replace the last ',' from the file $json = $this->str_lreplace(',', '', $json); $json = json_decode($json); if($json === null) { die("JSON is invalid."); } // initialise structure array $structure = array(); // foreach of the json prospectus keys foreach($json->prospectus as $obj => &$data) { // explode on the sectionParent and take second last element (this is the actual parent from the URL) $data->sectionParent = explode('/', $data->sectionParent); $data->sectionParent = $data->sectionParent[count($data->sectionParent) - 2]; if(!isset($structure[$data->sectionParent]['display_title']) || ($structure[$data->sectionParent]['display_title'] !== CONFIG::getFirstPage() && $structure[$data->sectionParent]['display_title'] !== CONFIG::getLastPage())) { $data->sectionParent = strtolower($data->sectionParent); $data->sectionParent = replaceCommasHyphens($data->sectionParent); $data->sectionTitle = replaceCommasHyphens((string)$data->sectionTitle); // if the sectionParent doesn't have a key in the structure array if(!isset($structure[$data->sectionParent])) { // set the array up with a total_ranges value of the current section ranges $structure[$data->sectionParent] = array ('total_ranges' => $data->sectionRanges ); } else if($structure[$data->sectionParent]['total_ranges'] !== ''){ // else just append onto what already exists $structure[$data->sectionParent]['total_ranges'] .= ',' . $data->sectionRanges; } else { // else set the total_ranges to the section_ranges value $structure[$data->sectionParent]['total_ranges'] = $data->sectionRanges; } if($data->sectionParent !== $data->sectionTitle) { $thisArray = array(); $thisArray[$data->sectionTitle] = $data->sectionRanges; $structure[$data->sectionParent] = array_merge($structure[$data->sectionParent], $thisArray); } $structure[$data->sectionParent]['display_title'] = $data->sectionParentTitle; } } $structure['md5_checksum'] = md5(file_get_contents(CONFIG::getJSON_FILE())); return $structure; } function str_lreplace($search, $replace, $subject) { $pos = strrpos($subject, $search); if($pos !== false) { $subject = substr_replace($subject, $replace, $pos, strlen($search)); } return $subject; } } class CacheHandler { public static function CacheUpToDate($json) { if(CONFIG::getENABLE_CACHING() === false) { return false; } $cached_file = CONFIG::getJSON_CACHED_FILE(); // if the cached file exists if(file_exists($cached_file)) { $json_cached = json_decode(file_get_contents($cached_file), true); $md5_main_json = md5($json); // if the MD5 strings are the same, return the cached version if($json_cached['md5_checksum'] === $md5_main_json) { return $json_cached; } else { // else return false return false; } } else { // else return false return false; } } public static function WriteNewerCache($structure) { if(CONFIG::getENABLE_CACHING() === false) { return false; } if(file_exists(CONFIG::getJSON_CACHED_FILE())) { // we write the cached file file_put_contents(CONFIG::getJSON_CACHED_FILE(), json_encode($structure)); } else { // else we make the directory, then write the cached file $dir_to_write_json = CONFIG::getPROSPECTUS_BUILDER_LOCATION() . '/' . CONFIG::getJSON_CACHED_FILE(); $dir_to_write_json = str_replace('\\', '/', $dir_to_write_json); $dir_to_write_json = str_replace('/index.json', '', $dir_to_write_json); // check if the directory exists if(is_dir($dir_to_write_json)) { // write the file file_put_contents(CONFIG::getJSON_CACHED_FILE(), json_encode($structure)); } else { if(mkdir($dir_to_write_json) === false) { die("error creating directory"); } else { // we write the file file_put_contents(CONFIG::getJSON_CACHED_FILE(), json_encode($structure)); } } } } } class send_emails { private $email_add; private $port; private $from; private $content; function __construct($email_addr) { ini_set('SMTP', CONFIG::getSMTP_HOST()); ini_set('smtp_port', CONFIG::getSMTP_PORT()); $this->sendEmailToEndUser($email_addr); $selections = $this->getUserSelections($_POST['selections']); $this->sendEmailToSite($selections, $email_addr); } private function getUserSelections($selections) { $selections = str_replace('-=-', ' ', $selections); $selections = ucwords($selections); return $selections; } private function sendEmailToSite($selections, $email_addr) { $this->from = CONFIG::getEmail_Address_From(); $this->msg = $this->formatSiteEmailMessage($_POST['selections'], $email_addr); $this->headers = "MIME-Version: 1.0" . "\r\n" . "Content-type: text/html; charset=UTF-8" . "\r\n" . "From: " . $this->from . "\r\n" . "Reply-To: " . $this->from . "\r\n" . 'X-Mailer: PHP/' . phpversion(); $this->subject = "User downloaded a Prospectus"; $client_email_address = CONFIG::getEmail_Address_Site(); $sent = mail($client_email_address, $this->subject, $this->msg, $this->headers); if($sent === false) { die("false"); } } private function sendEmailToEndUser($email_addr) { $this->from = CONFIG::getEmail_Address_From(); $this->msg = $this->getEndUserEmailMessage(); $this->subject = $this->getEndUserEmailSubject(); $validEmail = filter_var($email_addr, FILTER_VALIDATE_EMAIL); if($validEmail === false) { die('Please enter a valid email address'); } if($this->msg && $this->subject) { $this->headers = "MIME-Version: 1.0" . "\r\n" . "Content-type: text/html; charset=UTF-8" . "\r\n" . "From: " . $this->from . "\r\n" . "Reply-To: " . $this->from . "\r\n" . 'X-Mailer: PHP/' . phpversion(); $sent = mail($email_addr, $this->subject, $this->msg, $this->headers); if($sent === false) { die("Failed to attempt sending email"); } } } function formatSiteEmailMessage($subs, $end_user_email) { $msg = "A user downloaded a prospectus with an email address of " . $end_user_email; $msg .= "

List of topics included in the Prospectus were:
"; //$selections = implode('
', $subs ); $selections = ''; // add blank value to subs, in case only one value was selected. $subs[] = ''; foreach($subs as &$selection) { $selection = str_replace('-=-', ' -> ', $selection); $selection = str_replace('_', ' ', $selection); $selection = ucwords($selection); $selections .= $selection . '
'; } $msg .= $selections; return $msg; } function getEndUserEmailMessage() { return CONFIG::getEndUserMessage(); } function getEndUserEmailSubject() { return CONFIG::getEndUserSubject(); } } class GeneratePDF { public $ranges; public $pdf; function __construct($choices, $json_data) { $this->json_data = $json_data; $this->ranges = $this->getRanges($choices, $json_data); $this->ranges = $this->formatRanges($this->ranges); // check if the first and past page data is set, and if so, include it. $this->ranges = $this->addFirstLastPage($this->ranges, $json_data); $this->pdf = $this->returnPDF($this->ranges); } function getRanges($choices, $json_data) { $ranges = ''; // add compulsory, if exists if(isset($json_data->json_data[CONFIG::getCOMPULSORY_SECTION()])) { $ranges .= $json_data->json_data[CONFIG::getCOMPULSORY_SECTION()]['total_ranges'] . ','; } // loop over the user selections foreach($choices['selections'] as $index => $course_selections) { // flag to append comma or not $append_comma = true; // if there is no hyphen, we are dealing with a main course if(strpos($course_selections, '-=-') === false) { $ranges .= $json_data->json_data[$course_selections]['total_ranges']; } else { // else we explode by the hyphen to get the main course and sub course name $sections = explode('-=-', $course_selections); $course = $sections[0]; $sub_section = $sections[1]; if(!in_array($course, $choices['selections'])) { $ranges .= $json_data->json_data[$course][$sub_section]; // we also need to include the compulsory section, if it is set if(CONFIG::getCOMPULSORY_SECTION() && isset($json_data->json_data[$course][CONFIG::getCOMPULSORY_SECTION()]) && strpos($ranges, $json_data->json_data[$course][CONFIG::getCOMPULSORY_SECTION()]) === false ) { $ranges .= ',' . $json_data->json_data[$course][CONFIG::getCOMPULSORY_SECTION()]; } } else { $append_comma = false; } } // if we are not on the last index of the choices, add a trailing comma if($index !== count($choices['selections']) -1 && $append_comma == true) { $ranges .= ','; } /* // we also need to include the compulsory section, if it is set if(CONFIG::getCOMPULSORY_SECTION() && isset($json_data->json_data[$course][CONFIG::getCOMPULSORY_SECTION()]) && strpos($ranges, $json_data->json_data[$course][CONFIG::getCOMPULSORY_SECTION()]) === false ) { $ranges .= $json_data->json_data[$course][CONFIG::getCOMPULSORY_SECTION()] . ','; } */ } // return the ranges return $ranges; } /** * Format the $range so that it is a comma delimitted string and replace all ranges with singular values * that are comma delimitted **/ function formatRanges($ranges) { $ranges_array = array(); $ranges_array = explode(',', $ranges); $ranges_array = array_filter($ranges_array); foreach($ranges_array as &$range) { // if there is a range if(strpos($range, '-') !== false) { // Explode by the hyphen $numbers = explode('-', $range); // Ensure the data is numeric, there are only 2 numbers, and the number on the left is less than the right if((is_numeric($numbers[0]) && is_numeric($numbers[1]) && count($numbers) == 2) && ($numbers[0] < $numbers[1])) { $new_range = ''; // Create the new page numbers with comma delimmitted values for($i = $numbers[0]; $i<= $numbers[1]; $i++) { $new_range .= $i; if($i != $numbers[1]) { $new_range .= ','; } } $range = $new_range; } else { // there was an error with the data $this->throw_range_error(); } } } // implode by comma $ranges = implode(',', $ranges_array); // explode again by comma $ranges = explode(',', $ranges); // make ranges unique (remove duplicates so that same pages dont appear twice) $ranges = array_unique($ranges); // sort ranges so pages appear in order sort($ranges, SORT_NUMERIC ); // reimplode and return $ranges = implode(',', $ranges); return $ranges; } function addFirstLastPage($ranges, $json_data) { if(CONFIG::getFirstPage() && CONFIG::getLastPage()) { $first_page_name = CONFIG::getFirstPage(); $last_page_name = CONFIG::getLastPage(); $first_page_range = $json_data->json_data[$first_page_name]['total_ranges']; $last_page_range = $json_data->json_data[$last_page_name]['total_ranges']; $ranges = $first_page_range . ',' . $ranges . ',' . $last_page_range; return $ranges; } else { return $ranges; } } function returnPDF($ranges, $type = 'download') { require_once(CONFIG::getPDF_PHP_LIBRARY_FILE()); $pdf = new PDFMerger; try { $pdf->addPDF(CONFIG::getMAIN_PDF_FILE(), $ranges) ->merge($type, CONFIG::getPDF_NAME()); } catch (Exception $e) {} return $pdf; } function checkExists($string, $json_data) { } function formatChoices($choices) { } function throw_range_error() { die("Data error."); } } class GetHTML { public $html; private $json_data; private $parent_class; private $child_class; function __construct($json_data) { $this->parent_class = CONFIG::getHTML_PARENT_CLASS(); $this->child_class = CONFIG::getHTML_CHILD_CLASS(); $this->json_data = $json_data; $this->html = $this->generateHTML($this->json_data); echo $this->html; } function generateHTML($json_data) { $html = ''; $html .= ""; $html .= '
'; if(CONFIG::getSHOW_LIGHTBOX()) { $html .= ' '; $header_lightbox_text = CONFIG::getLightboxHeaderText(); if(CONFIG::getUseDefaultLightboxStyle()) { $html .= ""; } $html .= "

{$header_lightbox_text}





"; } foreach($json_data as &$array) { // Remove the 'MD5_CHECKSUM' key if it is set if(isset($array['md5_checksum'])) { unset($array['md5_checksum']); } $counter = 1; foreach($array as $main_heading => $data) { if(replaceCommasHyphens(CONFIG::getFirstPage()) !== $main_heading && replaceCommasHyphens(CONFIG::getLastPage()) !== $main_heading) { $html .= <<
{$data['display_title']}
  Select All
  Remove All
HTML; foreach($data as $option => $ranges) { // don't include the total_ranges sections or the compulsory sections in the HTML if($option !== 'total_ranges' && $option !== 'display_title' /*&& CONFIG::getCOMPULSORY_SECTION() !== false*/ && $option !== CONFIG::getCOMPULSORY_SECTION()) { $title = replaceUnderscoreWithSpace($option); $html .= << {$title}
  Include
  Added
HTML; } } $html .= '
'; $counter++; } } $html .= ''; $html .= ''; } return $html; } } function replaceCommasHyphens($string) { $string = str_replace(',', "_", $string); $string = str_replace('-', "_", $string); $string = str_replace(' ', "_", $string); return $string; } function replaceUnderscoreWithSpace($string) { $string = str_replace('_', ' ', $string); return $string; } ?>
A generic image of stone heap, also know as Isivivane
Your Journey Begins Here

Apply to Join Rhodes University

Step into a legacy of excellence, leadership, and transformation. At Rhodes University, we don’t just educate—you become part of a powerful network of thinkers, creators, and changemakers rooted in the Eastern Cape and reaching across the world. Whether you're applying for undergraduate, postgraduate, or international study, this is where your future begins.

Invest in the Future of South Africa

Support Rhodes University Today

Your donation helps us empower the next generation of leaders, innovators, and change agents. At Rhodes University, every gift—big or small—makes a real impact: funding student bursaries, advancing cutting-edge research, and preserving the spirit of academic excellence in the heart of Makhanda. Together, we can transform lives and build a stronger future.

A generic image of stone heap, also know as Isivivane