Word Count: 4795
  • Post category:PrimeCare
  • Post last modified:2022-06-29

Functional Requirements

  • Allow site visitors to input child name and dob, select a doctor, then generates the PDF file showing the suggested dates for vaccinations.
  • Allow Admin to enter the vaccination timetable details for each doctor via the ‘Our Team’ post.
  • Allow Admin to seach the visitor records, and download these records in the CSV format.

Setup

  1. Create custom fields for the vaccines for ‘Our Team’ CPT.
    1. Enter the suggested durations from birth (or ages) for various vaccines for each doctor via the post editor.
  2. Create the ‘vaccine-program’ directory under the active child theme:
    1. vaccine-program/vaccine-functions.php
      1. the file provides the required functions.
      2. $vacc_termAAr – is an array that contains the mapping of the vaccine field names to the Chinese and English translations.
      3. $ageAAr – is an array that contains the mapping of duration codes to the Chinese and English translations, and the duration in days from the date of birth (dob). The duration is used to calculate the ‘suggested vaccination date’.
    2. vaccine-program/dompdf/
      1. the directory to store the dompdf library.
      2. https://dompdf.github.io/
      3. https://github.com/dompdf/dompdf
    3. vaccine-program/fonts/
      1. the directory to store the ‘fireflysung-130’ font which is required by the dompdf to generate Chinese characters.
      2. http://luc.devroye.org/fonts-51316.html
    4. vaccine-program/temp_csv/
      1. the directory to store the csv files for download of the visitor records. The max number of files in this directory is controlled by the function control_temp_files( ).
    5. vaccine-program/temp_files/
      1. the directory to store the temporary files for creation of PDF files. The max number of files in this directory is controlled by the function control_temp_files( ).
  3. The following new pages are created with their own page template files:
    1. Vaccine Program Request => page-vaccine-program-request.php
      1. Input form for visitors.
    2. Vaccine Program Display => page-vaccine-program-display.php
      1. Display the vaccination program and suggested vaccination dates.
    3. Vaccine Program PDF => page-vaccine-program-pdf.php
      1. Generate and display the PDF file.
    4. Vaccine Submitted Records => page-vaccine-submitted-records.php
      1. Search form for Admin
      2. Display search results
    5. Vaccine Submitted CSV Download => page-vaccine-submitted-csv-download.php
      1. Download the CSV file.
      2. Note: the CSV file needs to add the Byte-Order-Marker (BOM) at the beginning of the file in order to allow Excel to display Chinese correctly when the file is open by double click.

Directory Sturcture

Codes

page-vaccine-program-request.php

<?php
/*
Template Name: Vaccine Timetable Request Template

Base on the Full-Width Template (full-width.php)
*/

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

get_header(); ?>


<?php if ( astra_page_layout() == 'left-sidebar' ) : ?>

	<?php get_sidebar(); ?>

<?php endif ?>


<style>
	#content {
		background: url(/wp-content/uploads/2021/09/survey-bg.jpg) no-repeat;
		background-position: center;
		background-size: cover;
	}

	.vt_form_container {
		width: 40%; 
		margin-left: auto;
		margin-right: auto;
		margin-top: 70px;
		margin-bottom: 70px;
		padding: 30px;
		background: #fff;
		border-radius: 10px;
	}

	.vt_form_container h1 {
		margin-bottom: 20px;
	}
	.vt_form input, .vt_form select {
		margin-bottom: 10px; 
	}
	.vt_form input[type='date'] {
		width: 100%;
		height: 50px;
		color: #666;
    	padding: 0.75em;
		box-sizing: border-box;
		border-width: 1px;
		border-style: solid;
		border-color: #eaeaea;
		border-radius: 2px;
		background: #fafafa;
		box-shadow: none;
	}
	.vt_form input[type='submit'] {
		color: #fff;
		background: #8f6c95;
		border-radius: 10px;
		width: 100%;
		margin-top: 20px;
	}
	.vt_form input[type='submit']:hover {
		background: #42a7c6;
	}

	@media(max-width: 1024px) {
		#content { padding-top: 70px; padding-bottom: 70px; }

		.vt_form_container { width: 70%; }
	} /* w1024 */


	@media(max-width: 767px) {
		#content { padding-top: 50px; padding-bottom: 50px; }
		.vt_form_container { width: 90%; }
	} /* w767 */
</style>

	<div id="primary" <?php astra_primary_class(); ?>>

		<?php astra_primary_content_top(); ?>

		<?php astra_content_page_loop(); ?>

		<?php astra_primary_content_bottom(); ?>

		<div class="vt_form_container">
			<h1>建立注射疫苗時間表</h1>			

			<form action="<?= get_site_url(). '/vaccine-program-display'; ?>" method="post" class="vt_form">
				<div class="form_div">
					<label for="parent_name">家長姓名</label>
					<input type="text" name="parent_name" required/>
				</div>
				<div class="form_div">
					<label for="email">家長電郵</label>
					<input type="email" name="email" required/>
				</div>
				<div class="form_div">
					<label for="phone">家長電話</label>
					<input type="text" name="phone" pattern="[0-9]{8,11}" required/>
				</div>
				<div class="form_div">
					<label for="child_name">小孩姓名</label>
					<input type="text" name="child_name" required/>
				</div>
				<div class="form_div">
					<label for="child_bday">小孩出生日期</label>
					<input type="date" name="child_bday" required/>
				</div>
				
				<div class="form_div">
					<label for="selected_dr">選擇醫生</label>
					<select name="selected_dr" required>
						<option value="" disabled selected> -- 請選擇 -- </option>
						
					<?php 
						$args = array (
							'post_type' => 'our_team',
							'post_status' => 'publish',
							'posts_per_page' => -1 ,
							'meta_key' => 'sort_rank',
							'orderby' => 'meta_value',
							'order' => 'ASC',
							'tax_query' => array(
												'taxonomy' => 'dr_team',
												'field'=> 'term_id',
												'terms'=> 33 //兒科專科團隊
											)
						);
						
						
						$filtered_drs = new WP_Query($args);?>

						<?php if ($filtered_drs->have_posts()): ?>
							<?php while ($filtered_drs->have_posts()): ?>
								<?php 
									$filtered_drs->the_post();
									global $post;

									if( get_field('has_vaccine_timetable') ):
										$postID = get_the_ID();
										$dr_en = get_field('dr_name_en');
										$dr_zh = get_field('dr_name_zh');
								?>
									<option value="<?=$postID?>"><?=$dr_zh?> <?=$dr_en?></option>
								<?php endif;?>
							<?php endwhile; ?>
						<?php endif ;?>

					</select>
				</div>

				<input type="submit" value="提交">
			</form>

		</div> <!-- vt_form_container -->
		

	</div><!-- #primary -->

<?php if ( astra_page_layout() == 'right-sidebar' ) : ?>

	<?php get_sidebar(); ?>

<?php endif ?>

<?php get_footer(); ?>

page-vaccine-program-display.php

<?php 

/**
 * 
 * 
 */

if ( ! defined('ABSPATH') || !isset($_POST['selected_dr'])) {
	exit("<br><br><br>Access denied...!"); // Exit if accessed directly.
}

include_once get_stylesheet_directory(). '/vaccine-program/vaccine-functions.php';

get_header();

date_default_timezone_set('Asia/Hong_Kong');

$p_name = sanitize_text_field($_POST['parent_name']);
$p_email = sanitize_text_field($_POST['email']);
$p_phone = sanitize_text_field($_POST['phone']);
$c_name = sanitize_text_field($_POST['child_name']);
$c_bday = sanitize_text_field($_POST['child_bday']);
$dr_id = $_POST['selected_dr'];


$dr_tcname = get_field('dr_name_zh', $dr_id);
$dr_enname = get_field('dr_name_en', $dr_id);

$fieldsAAr = get_fields($dr_id);
$vaccDateAAr = array();

foreach($fieldsAAr as $k=> $v) {
	if (substr($k, 0, 4)==='INJ_') {
		$vaccDateAAr[$v][]=$k;
	}
}

ksort($vaccDateAAr);

if (array_key_first($vaccDateAAr) == NULL) array_shift($vaccDateAAr); //remove first element which has key=null

ob_start();
?>

<div class="cu-vacc-program-section">
	<h3>兒童疫苗接種計劃</h3>
	<h3>Child Vaccination Program</h3>
	<table class="cu-vacc-table1 mobile_hidden">
		<tr>
			<td class='cu-tb1-c_name-label cu-tb1-label'>兒童姓名<br>Child Name</td>
			<td class='cu-tb1-c_name-value'><?=$c_name?></td>
			<td class='cu-tb1-bday-label cu-tb1-label'>兒童出生日期<br>Child Birthday</td>
			<td class='cu-tb1-bday-value'><?=$c_bday?></td>
			<td class='cu-tb1-prtday-label cu-tb1-label'>印列日期<br>Printed date</td>
			<td class='cu-tb1-prtday-value'><?=date('Y-m-d')?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>父母姓名<br>Parent Name</td>
			<td><?=$p_name?></td>
			<td class='cu-tb1-label'>電郵地址<br>Email Address</td>
			<td><?=$p_email?></td>
			<td class='cu-tb1-label'>電話<br>Phone</td>
			<td><?=$p_phone?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>醫生<br>Doctor</td>
			<td colspan="5"><?=$dr_tcname?><br><?=$dr_enname?></td>
		</tr>
	</table>

<?php
	$html_1 = ob_get_flush();
?>

	<table class="cu-vacc-table1 mobile_shown">
		<tr>
			<td class='cu-tb1-label'>兒童姓名<br>Child Name</td>
			<td><?=$c_name?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>兒童出生日期<br>Child Birthday</td>
			<td><?=$c_bday?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>父母姓名<br>Parent Name</td>
			<td><?=$p_name?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>電郵地址<br>Email Address</td>
			<td><?=$p_email?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>電話<br>Phone</td>
			<td><?=$p_phone?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>醫生<br>Doctor</td>
			<td><?=$dr_tcname?><br><?=$dr_enname?></td>
		</tr>
		<tr>
			<td class='cu-tb1-label'>印列日期<br>Printed date</td>
			<td><?=date('Y-m-d')?></td>
		</tr>
	</table>

	
<?php 
	ob_start();
	echo create_timetable($vaccDateAAr, $dr_tcname, $dr_enname, $ageAAr, $c_bday, $vacc_termAAr);
	$html_2 = ob_get_flush();	
?>

</div>  <!-- .cu-vacc-program-section -->

<?php 
	$html_content = $html_1. $html_2;

	$temp_file = get_stylesheet_directory(). "/vaccine-program/temp_files/". date('YmdHi'). "-$p_email-". rand(100, 999). ".tmp";

	file_put_contents($temp_file, $html_content);

?>


<div class="cu-vacc-program-section cu-btn-getPDF">
	<form method="post" target="_blank" action="<?= get_site_url(). '/vaccine-program-pdf/'; ?>">
		<input type="hidden" name="temp_file" value="<?=$temp_file; ?>">
		<input type="hidden" name="child_name" value="<?=$c_name; ?>">
		<input type="hidden" name="dr_id" value="<?=$dr_id; ?>">
		<button type="submit">提取PDF檔案<br>Get PDF File</button>			
	</form>

</div> <!-- .cu-vacc-program-section -->

<style>
	html, body {
		width:100%;
		overflow-x: hidden;
	}

	.ast-container {
		flex-direction: column;
	}


	.cu-vacc-program-section {
		width: 100%;
		max-width: 1200px;
		margin: 0 auto;
		padding: 60px 0px 0px;
		overflow: hidden;
		box-sizing: border-box;
	}

	.cu-vacc-program-section h3 {
		text-align: center;
		font-size: 22px;
		font-weight: 500;
		color: #7F4F21;
	}

	.cu-vacc-program-section h3:last-of-type {
		margin-bottom: 30px;
	}

	.cu-tb1-label {
		background-color: #42a7c6;
		color: #fff;
	}

	.cu-vacc-table1 td {
		font-size: 16px;
		line-height: 1.2;
	}

	.cu-vacc-table1 td:nth-of-type(2) {
		width: 20%;
	}

	.cu-vacc-table1 td:nth-of-type(4) {
		width: 22%;
	}

	.cu-vacc-table1 td:nth-of-type(5),
	.cu-vacc-table1 td:nth-of-type(6) {
		width: 13%;
	}


	/*** Table 2 */
	.cu-vacc-table2 {
		margin-top: 80px;
		border-collapse: collapse;
	}

	.cu-vacc-table2 tr td:nth-of-type(1) {
		width: 14%;
	}

	.cu-vacc-table2 tr td:nth-of-type(2) {
		width: 13%;
	}

	.cu-vacc-table2 tr td:nth-of-type(3) {
		width: 73%;
	}

	.cu-vacc-table2 td {
		font-size: 15px;
		line-height: 1.4;
		padding: 18px 10px;
		vertical-align: top;
		border-width: 1px;
	}

	.cu-vacc-table2 td.cu-tb2-col-age,
	.cu-vacc-table2 td.cu-tb2-col-date {
		text-align: center;
	}

	.cu-vacc-table2 td ul {
		list-style-position: outside;
		list-style: none;
		position: relative;
		margin-left: 0px;
		padding: 0;
	}

	.cu-vacc-table2 td ul:last-of-type {
		margin-bottom: 0px;
	}

	.cu-vacc-table2 ul li {
		padding: 2px 0 2px 18px;
	}

	.cu-vacc-table2 ul li::before {
		position: absolute;
		left: 0;
		content: '-';
	}

	.cu-tb2-header td {
		font-weight: 500;
		text-align: center;
		background-color: #8f6c95;
		color: #fff;
	}

	.cu-tb2-vacc-en li {
		line-height: 1.2;
	}

	.cu-vacc-program-section p {
		margin-bottom: 6px;
		font-size: 12px;
	}


	.cu-btn-getPDF {
		text-align: center;
		margin-bottom: 80px;
	}

	.cu-vacc-program-section input[type='submit'] {
		color: #505050;
		width: 200px;
	}

	.cu-btn-getPDF button {
		color: #fff;
		width: 220px;
		line-height: 1.4;
		background-color: #8f6c95;
		border-radius: 10px
	}
	.cu-btn-getPDF button:hover {
		background-color: #42a7c6;
	}

	.mobile_hidden { display: table; }
	.mobile_shown { display: none; }


	@media (max-width: 600px) {
		.ast-container {
			padding-left: 15px !important;
			padding-right: 15px !important;
		}

		.mobile_hidden { display: none; }
		.mobile_shown { display: block; }

		
		.cu-vacc-table1 tr td:first-child {
			width: 20%;
		}

		.cu-vacc-table2 td {
			font-size: 12px;
		}

		.cu-vacc-table2 ul li {
			padding-left: 10px;
		}

	}  /*  @600  */

</style>


<?php get_footer();?>

<?php

	/**
	 * Database table operations
	 */

	global $wpdb;
	require_once(ABSPATH . 'wp-admin/includes/upgrade.php');

	$tb_name = $wpdb->prefix. 'vaccine_program';
	$created = date('Y-m-d H:i:s');

	if ($wpdb -> get_var("SHOW TABLES LIKE '$tb_name'") != $tb_name): 
		$db_collate = $wpdb->collate;
		$sql = 
			"CREATE TABLE {$tb_name} (
			prog_id	INT UNSIGNED AUTO_INCREMENT,
			parent_name VARCHAR(100),
			email VARCHAR(50),
			phone VARCHAR(20),
			child_name VARCHAR(100),
			child_dob VARCHAR(20),
			doctor_id VARCHAR(20),
			doctor_cn VARCHAR(100),
			doctor_en VARCHAR(80),
			created VARCHAR(30),
			PRIMARY KEY  (email,child_name,doctor_id),
			KEY (doctor_cn),
			KEY (prog_id)
			)
			COLLATE {$db_collate}";

		dbDelta($sql);

	endif;

	$data = array('parent_name' => $p_name, 'email' => $p_email, 'phone' => $p_phone,
			'child_name' => $c_name, 'child_dob' => $c_bday, 
			'doctor_id' => $dr_id, 'doctor_cn' => $dr_tcname, 'doctor_en' => $dr_enname,
			'created' => $created );	

	$updated = $wpdb->update($tb_name, $data, array('email' => $p_email, 'child_name' => $c_name, 'doctor_id' => $dr_id));
	
	if ($updated === FALSE || $updated < 1) {
		$inserted = $wpdb->insert($tb_name, $data);
	}

	/**
	 * Temp files maintenance:
	 * 	Keep the latest 30 temp files and remove the older ones.
	*/
	control_temp_files("temp_files", 20);

?>

page-vaccine-program-pdf.php

<?php
/**
 * Use the DOMPDF library to turn HTML to PDF.
 * 
 * 1. Read in a tmp file which contains the content HTML and store it to $dom_content.
 * 2. Store logo image from Output Buffer to variable $dom_logo.
 * 3. Store the page number helper function from Output Buffer to variable $dom_footer.
 * 
 * Combine the three variables into $dom_full which is passed to the DOMPDF library.
 * 
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit('Error: Illegal Access...!'); // Exit if accessed directly.
}

if (! isset($_POST['temp_file'])) {
	exit("Error: Incomplete input...!");
}


require_once get_stylesheet_directory(). '/vaccine-program/dompdf/autoload.inc.php';

$temp_filename = $_POST['temp_file'];
$c_name = $_POST['child_name'];
$dr_id = $_POST['dr_id'];


$dom_content = file_get_contents($temp_filename);
ob_start();
?>
<div class="cu-logo-container">	
	<img src="<?= get_site_url(). '/wp-content/uploads/2021/08/primecare.png'; ?>" title="primecare" alt="primecare"> 
</div><!-- .cu-logo-container -->

<?php
	$dom_logo = ob_get_clean();
?>

<div class="cu-pdf-footer">
	<div class="cu-pdf-page-num">
		<span class="cu-p_number">- </span>
	</div> <!-- .cu-pdf-page-num -->
</div><!-- .cu-pdf-footer -->
<style>

	@font-face {
      font-family: 'Firefly Sung';
      font-style: normal;
      font-weight: 400;
      src: url(<?=get_site_url(). '/wp-content/themes/astra-child/vaccine-program/fonts/fireflysung-130/fireflysung.ttf'; ?>) format('truetype');
    }

	
    * {
      font-family: 'Firefly Sung', 'DejaVu Sans', sans-serif;
    }


	html {
		margin: 0cm;
	}

	body {
		margin: 2.8cm 0.5cm 1.5cm;
		width: 20cm;
	}

	table {
		border-collapse: collapse;
		width: 19cm;
	}

	table th,
	table td {
		border: 1px solid #505050;
		padding: 0.2cm 0.1cm;
		font-size: 0.3cm;
		word-wrap: break-word;
		overflow-wrap: break-word;
	}


	.cu-logo-container {
		position: fixed;
		left: 1cm;
		top: 0.5cm;
	}

	.cu-logo-container img {
		width: 5cm;
	}
	

	.cu-pdf-footer {
		position: fixed;
		bottom: 0cm;
		left: 0cm;
		right: 0cm;
		height: 0.9cm;
		font-size: 0.3cm;
	}

	.cu-pdf-page-num {
		text-align: center;
	}

	.cu-p_number:after { 
		content: counter(page)' -'; 
	}

	.cu-vacc-program-section {
		margin-top: 0cm;
	}

	.cu-vacc-program-section h3 {
		font-family: 'Firefly Sung', 'DejaVu Sans', sans-serif;
		font-size: 0.45cm;
		font-weight: 400;
		margin: 0;
		line-height: 1.2;
		text-align: center;
	}

	.cu-vacc-table1 {
		margin: 1cm auto 0;
	}
	.cu-vacc-table1 .cu-tb1-label {
		background-color: #42a7c6;
		color: #fff;
	}

	.cu-tb1-c_name-label,
	.cu-tb1-prtday-label {
		width: 2.8cm;
	}

	.cu-tb1-c_name-value,
	.cu-tb1-bday-value {
		width: 3.3cm;
	}

	.cu-tb1-bday-label {
		width: 3cm;
	}

	.cu-tb1-prtday-value {
		width: 2.6cm;
	}

	.cu-tb1-label {
		background-color: #eee;
	}
	
	.cu-vacc-table2 {
		margin: 1cm auto 0;
		width: 19cm;
	}

	.cu-tb2-header td {
		text-align: center;
		background-color: #8f6c95;
		color: #fff;
	}

	.cu-tb2-age,
	.cu-tb2-col-age {
		width: 3.0cm;
	}

	.cu-tb2-drname,
	.cu-tb2-col-vacc {
		width: 12.0cm;
		word-wrap: break-word;
		overflow-wrap: break-word;
	}

	.cu-tb2-col-vacc {
		vertical-align: top;
	}

	.cu-tb2-date,
	.cu-col-date {
		width: 3.2cm;
	}

	.cu-tb2-col-age,
	.cu-tb2-col-date {
		text-align: center;
		vertical-align: top;
	}

	.cu-vacc-table2 td ul {
		list-style-position: outside;
		list-style: none;
		position: relative;
		margin: 0cm 0cm 0.5cm;
		padding: 0;
	}

	.cu-vacc-table2 td ul:last-of-type {
		margin-bottom: 0cm;
	}

	.cu-vacc-table2 ul li {
		padding: 0.05cm 0 0.05cm 0.3cm;
	}

	.cu-vacc-table2 ul li::before {
		position: absolute;
		left: 0;
		content: '-';
	}

</style>

<?php
$dom_footer = ob_get_clean();
$dom_full = $dom_logo. $dom_footer. $dom_content;

use Dompdf\Dompdf;
use Dompdf\Options;

$options = new Options();
$options->set('isRemoteEnabled', true);

$pdf_fname = "Vacc-$c_name-$dr_id.pdf";

$dompdf = new Dompdf($options);

$dompdf->loadHtml($dom_full);
$dompdf->setPaper('A4', 'portrait');

$dompdf->render();
$dompdf->stream($pdf_fname, array('Attachment'=>0));

?>

page-vaccine-submitted-records.php

<?php 
/**
 * Display records from db table wp_vaccine_program.
 * 
 * Functions:
 * 1. 'seach' for selected fields.
 * 2. 'reset' of search form.
 * 3. 'CSV' for CSV file download.
 * 
 * 
 */

if ( ! defined('ABSPATH')) {
	exit; // Exit if accessed directly.
}

include_once get_stylesheet_directory(). '/vaccine-program/vaccine-functions.php';
date_default_timezone_set('Asia/Hong_Kong');

get_header();

global $wpdb;
$headArr = array();
$notice_msg = '';
$where = "";

if (isset($_POST['search'])) {

	$where = clean_create_sql_where($_POST);

} elseif (isset($_POST['reset'])) {
	$_POST = array();
} 

$recordsAAr = $wpdb->get_results("SELECT * FROM {$TB_NAME} {$where} ORDER BY prog_id DESC LIMIT 100", ARRAY_A);

if (empty($recordsAAr)) {
	$notice_msg = "No records found...!";

} else {

	$notice_msg = sizeof($recordsAAr). " record(s) found.";

	$rec0Arr = $recordsAAr[0];
	foreach ($rec0Arr as $key => $value) {
		$headArr[] = $key;
	}
	
	$header = "<thead><tr><th>". join("</th><th>", $headArr). "</th></tr></thead>";
	
	$rowsAAr = array();
	
	foreach ($recordsAAr as $row) {
		$row_valsArr = array();
		foreach ($row as $key => $value) {
			$row_valsArr[] = $value;
		}
	
		$rowsAAr[] = "<tr><td>". join("</td><td>", $row_valsArr). "</td></tr>";
	}
	
}

?>

<div class="cu-record-page-wrapper">
	<h2>Submitted Vaccine Program Records<br><span>(max 100 records)</span></h2>

	<div class="cu-record-search">

		<form action="" method="post" class="cu-serach-form">
			<div class="cu-form-item">
				<label for="p_name">Parent Name: </label>
				<input type="text" name="parent_name" id="p_name" value="<?= isset($_POST['parent_name'])? $_POST['parent_name'] : ''?>">
			</div>
			<div class="cu-form-item">
				<label for="c_name">Child Name: </label>
				<input type="text" name="child_name" id="c_name" value="<?= isset($_POST['child_name'])? $_POST['child_name'] : ''?>">
			</div>
			<div class="cu-form-item">
				<label for="p_email">Email: </label>
				<input type="text" name="email" id="p_email" value="<?= isset($_POST['email'])? $_POST['email'] : ''?>">
			</div>
			<div class="cu-form-item">
				<label for="p_phone">Phone: </label>
				<input type="text" name="phone" id="p_phone" value="<?= isset($_POST['phone'])? $_POST['phone'] : ''?>">
			</div>
			<div class="cu-form-item">
				<label for="dr_en">Doctor EN: </label>
				<input type="text" name="doctor_en" id="dr_en" value="<?= isset($_POST['doctor_en'])? $_POST['doctor_en'] : ''?>">
			</div>

			<div class="cu-form-item cu-form-button">
				<button type="submit" name="search" value="search">Search</button>
				<button type="submit" name="download" value="csv" formaction="<?= get_site_url(). '/vaccine-submitted-csv-download/'; ?>">CSV</button>
				<button type="submit" name="reset" value="reset">Reset</button>

			</div>
		</form>


	</div> <!-- cu-record-search -->

	<div class="cu-record-container">

		<h3 class='cu-notice-msg'><?=$notice_msg?></h3>

		<?php if (! empty($recordsAAr)): ?>

		<table>
			<?=$header. "<tbody". join($rowsAAr). "</tbody>"; ?>
		</table>

		<?php endif;?>

	</div> <!-- cu-record-container -->

</div> <!-- cu-record-page-wrapper -->


<style>
	.cu-record-page-wrapper {
		width: 100%;
		max-width: 1200px;
		margin: 0 auto;
		padding: 50px 20px;
	}

	.cu-record-page-wrapper h2 {
		font-size: 22px;
		text-align: center;
		padding: 30px 0;
	}

	.cu-record-page-wrapper h2 span {
		font-size: 17px;
	}

	.cu-record-search {
		padding: 0 10px;
		margin: 20px 0 50px;
		border: 1px solid #505050;
	}

	.cu-serach-form {
		display: flex;
		flex-direction: row;
		flex-wrap: wrap;
		justify-content: space-between;
		font-size: 14px;
		line-height: 1.2;
		color: #505050;
	}

	.cu-serach-form .cu-form-item {
		width: 47%;
		padding: 20px 10px;
		display: flex;
		flex-direction: row;
		flex-wrap: nowrap;
		justify-content: space-between;
		align-items: center;
	}

	.cu-serach-form .cu-form-item label {
		width: 25%;
		font-weight: 600;
	}

	.cu-serach-form .cu-form-item input {
		width: 71%;
		height: 20px;
	}

	.cu-serach-form .cu-form-button {
		justify-content: space-around;
		padding: 10px;
	}

	.cu-serach-form .cu-form-item button {
		color: #505050;
		width: 30%;
		background-color: #eee;
	}

	.cu-serach-form .cu-form-item button[name='reset'] {
		color: #fefefe;
		width: 30%;
		background-color: #888;
	}

	.cu-record-container {
		overflow: auto;
	}

	.cu-notice-msg {
		text-align: center;
		margin: 20px auto;
		width: 90%;
		color: #8F6C95;
	}

	.cu-record-container th {
		text-align: center;
		font-size: 15px;
		font-weight: 600;
		background-color: #eee;
	}

	.cu-record-container td {
		font-size: 14px;
		line-height: 1.2;
	}

	.cu-record-container td:nth-of-type(1),
	.cu-record-container td:nth-of-type(7),
	.cu-record-container td:nth-of-type(10) {
		text-align: center;
	}


	@media (max-width: 600px) {
		.cu-serach-form .cu-form-item {
			width: 100%;
		}
	} /*  @600   */

</style>

<?php get_footer();?>

page-vaccine-submitted-csv-download.php

<?php
/**
 * 
 * 
 * 
*/

if ( ! defined( 'ABSPATH' ) ) {
	exit('E001: Illegal Access...!'); // Exit if accessed directly.
}

if ( $_POST['download'] != 'csv') {
	exit ('E002: Illegal Access...!');
}


include_once get_stylesheet_directory(). '/vaccine-program/vaccine-functions.php';

date_default_timezone_set('Asia/Hong_Kong');
global $wpdb;
$rows_per_csv_loop = 800;
$where = clean_create_sql_where($_POST);


// Find row count
$sql = "SELECT COUNT(prog_id) FROM {$TB_NAME} {$where}";
$row_count = $wpdb->get_var($sql);

if ($row_count > 0) {
	$loop_count = ceil($row_count / $rows_per_csv_loop);

	$csv_baseName = date('Ymd-Hi'). "-". rand(1000, 9999). ".csv";
	$csv_fullName = get_stylesheet_directory(). "/vaccine-program/temp_csv/$csv_baseName";

	$col_labels = array('prog_id', 'parent_name','email', 'phone', 'child_name', 'child_dob','doctor_id', 'doctor_cn', 'doctor_en', 'created');

	$f = fopen($csv_fullName, 'a+'); //append mode and put pointer to EOF

	fwrite($f, "\xEF\xBB\xBF");  // UTF-8 BOM (Byte-order Marker)

	fputcsv($f, $col_labels, ",");


	for ($i=0; $i<$loop_count; $i++) {

		$offset = $i * $rows_per_csv_loop;

		$recordsAAr = $wpdb->get_results("SELECT * FROM {$TB_NAME} {$where} ORDER BY prog_id ASC LIMIT {$offset}, {$rows_per_csv_loop}", ARRAY_A);

		foreach ($recordsAAr as $rowArr) {
			$row_value = array();
			foreach ($rowArr as $col => $value) {
				$row_value[] = $value;
			}
			fputcsv($f, $row_value, ",");
		}
	}
	fclose($f);
	header('Content-Type: text/csv; charset=UTF-8'); 
	header('Content-Disposition: attachment; filename="' . $csv_baseName . '";'); 
	readfile($csv_fullName);

	control_temp_files('temp_csv', 10);  //control temp files quantity

	exit;

} else { 

	get_header();
	?>

	<div style="width:420px; margin: 100px auto; padding:40px; border:1px solid #505050;text-align:center">
		<h3>No records found! <a style="text-decoration:underline" href="<?=get_site_url(). '/vaccine-submitted-records' ?>">Go Back</a></h3>
	</div>

	<?php 	
	get_footer();
} 

?>


vaccine-functions.php

<?php
/**
 * 
 */


$TB_NAME = $wpdb->prefix. 'vaccine_program';

 $vacc_termAAr = array (
	'INJ_BTMCV_1' => array(
		'tc' => 'B型腦膜炎雙球菌疫苗 - 第1針',
		'en' => 'Meningococcal B Vaccine - 1st Dose',
	),

	'INJ_BTMCV_2' => array(
		'tc' => 'B型腦膜炎雙球菌疫苗 - 第2針',
		'en' => 'Meningococcal B Vaccine - 2nd Dose',
	),

	'INJ_BTMCV_3' => array(
		'tc' => 'B型腦膜炎雙球菌疫苗 - 第3針',
		'en' => 'Meningococcal B Vaccine -  3rd Dose',
	),

	'INJ_DIH_2' => array(
		'tc' => '五合一疫苗 - 第2次 [乙型流感嗜血桿菌(Hib)、白喉、破傷風、無細胞型百日咳及小兒麻痺]',
		'en' => '5 in 1 Vaccine - 2nd Dose',
	),

	'INJ_DIH_BD' => array(
		'tc' => '五合一疫苗 - 加強劑',
		'en' => '5 in 1 Vaccine - Booster Dose',
	),

	'INJ_DIHH_BD' => array(
		'tc' => '白喉、破傷風、無細胞型百日咳(減量)及滅活小兒麻痺混合疫苗 - 加強劑',
		'en' => '4 in 1 Vaccine - Booster Dose',
	),

	'INJ_DTAP_BD' => array(
		'tc' => '四合一疫苗 - 加強劑',
		'en' => '4 in 1 Vaccine - Booster Dose',
	),

	'INJ_HA_1' => array(
		'tc' => '甲型肝炎疫苗 - 第1針',
		'en' => 'Hepatitis A Vaccine - 1st Dose',
	),

	'INJ_HA_2' => array(
		'tc' => '甲型肝炎疫苗 - 第2針',
		'en' => 'Hepatitis A Vaccine - 2nd Dose',
	),

	'INJ_JE_1' => array(
		'tc' => '日本腦炎疫苗-第1針 [乙腦]',
		'en' => 'Japanese Encephalitis Vaccine - 1st Dose [JE]',
	),

	'INJ_JE_2' => array(
		'tc' => '日本腦炎疫苗-第2針 [乙腦]',
		'en' => 'Japanese Encephalitis Vaccine - 2nd Dose [JE]',
	),

	'INJ_MCV_1' => array(
		'tc' => '四價腦膜炎雙球菌疫苗 (Nimenrix) - 第1針 [流腦]',
		'en' => 'Quadrivalent Meningococcal Vaccine (Nimenrix) - 1st Dose [meningococcal]',
	),

	'INJ_MCV_2' => array(
		'tc' => '四價腦膜炎雙球菌疫苗 (Nimenrix) - 第2針 [流腦]',
		'en' => 'Quadrivalent Meningococcal Vaccine (Nimenrix) - 2nd Dose [meningococcal]',
	),

	'INJ_MCV_3' => array(
		'tc' => '四價腦膜炎雙球菌疫苗 (Nimenrix) - 第3針 [流腦]',
		'en' => 'Quadrivalent Meningococcal Vaccine (Nimenrix) - 3rd Dose [meningococcal]',
	),

	'INJ_MMRV_1' => array(
		'tc' => '4痘疫苗MMRV - 第1次',
		'en' => 'Measles, Mumps, Rubella, Varicella (MMRV) vaccine - 1st Dose',
	),

	'INJ_MMRV_2' => array(
		'tc' => '4痘疫苗MMRV - 第2次',
		'en' => 'Measles, Mumps, Rubella, Varicella (MMRV) vaccine - 2nd Dose',
	),

	'INJ_ORV_1' => array(
		'tc' => '口服輪狀病毒疫苗 - 第1劑 [Rotarix / Rotateq]',
		'en' => 'Oral Rotavirus Vaccine - Dose 1 (Rotateq)',
	),

	'INJ_ORV_2' => array(
		'tc' => '口服輪狀病毒疫苗 - 第2劑 [Rotateq]',
		'en' => 'Oral Rotavirus Vaccine - Dose 2 (Rotateq)',
	),

	'INJ_ORV_3' => array(
		'tc' => '口服輪狀病毒疫苗 - 第3劑 [Rotateq]',
		'en' => 'Oral Rotavirus Vaccine - Dose 3 (Rotateq)',
	),

	'INJ_PCV_1' => array(
		'tc' => '13價肺炎球菌疫苗 - 第1次',
		'en' => 'Pneumococcal Conjugate Vaccine - 1st Dose',
	),

	'INJ_PCV_2' => array(
		'tc' => '13價肺炎球菌疫苗 - 第2次',
		'en' => 'Pneumococcal Conjugate Vaccine - 2nd Dose',
	),

	'INJ_PCV_3' => array(
		'tc' => '13價肺炎球菌疫苗 - 第3次',
		'en' => 'Pneumococcal Conjugate Vaccine - 3rd Dose',
	),

	'INJ_PCV_BD' => array(
		'tc' => '13價肺炎球菌疫苗 - 加強劑',
		'en' => 'Pneumococcal Conjugate Vaccine - Booster Dose',
	),

	'INJ_SIO_1' => array(
		'tc' => '六合一疫苗 - 第1次 [乙型肝炎疫苗 、乙型流感嗜血桿菌(Hib)、白喉、破傷風、無細胞型百日咳及小兒麻痺]',
		'en' => '6 in 1 Vaccine - 1st Dose',
	),

	'INJ_SIO_2' => array(
		'tc' => '六合一疫苗 - 第2次',
		'en' => '6 in 1 Vaccine - 2nd Dose',
	),

	'INJ_SIO_3' => array(
		'tc' => '六合一疫苗 - 第3次 [乙型肝炎疫苗、乙型流感嗜血桿菌(Hib)、白喉、破傷風、無細胞型百日咳及小兒麻痺]',
		'en' => '6-in-1 Vaccine - 3rd Dose',
	),

	'FHS_BCG' => array(
		'tc' => '卡介苗',
		'en' => 'BCG Vaccine',
	),

	'FHS_DTP_1' => array(
		'tc' => '白喉、破傷風、無細胞型百日咳及滅活小兒麻痺混合疫苗 - 第1次',
		'en' => 'DTaP-IPV Vaccine - 1st Dose',
	),

	'FHS_DTP_2' => array(
		'tc' => '白喉、破傷風、無細胞型百日咳及滅活小兒麻痺混合疫苗 - 第2次',
		'en' => 'DTaP-IPV Vaccine - 2nd Dose',
	),

	'FHS_DTP_3' => array(
		'tc' => '白喉、破傷風、無細胞型百日咳及滅活小兒麻痺混合疫苗 - 第3次',
		'en' => 'DTaP-IPV Vaccine - 3rd Dose',
	),

	'FHS_DTP_BD1' => array(
		'tc' => '白喉、破傷風、無細胞型百日咳及滅活小兒麻痺混合疫苗 - 加強劑',
		'en' => 'DTaP-IPV Vaccine - Booster Dose',
	),

	'FHS_DTP_BD2' => array(
		'tc' => '白喉(減量)、破傷風、無細胞型百日咳(減量)及滅活小兒麻痺混合疫苗 - 加強劑',
		'en' => 'DTaP-IPV Vaccine - Booster Dose',
	),

	'FHS_HB_1' => array(
		'tc' => '乙型肝炎疫苗 - 第1次',
		'en' => 'Hepatitis B Vaccine - 1st Dose',
	),

	'FHS_HB_2' => array(
		'tc' => '乙型肝炎疫苗 - 第2次',
		'en' => 'Hepatitis B Vaccine - 2nd Dose',
	),

	'FHS_HB_3' => array(
		'tc' => '乙型肝炎疫苗 - 第3次',
		'en' => 'Hepatitis B Vaccine - 3rd Dose',
	),

	'FHS_HP_1' => array(
		'tc' => '人類乳頭瘤病毒疫苗 - 第1次^',
		'en' => 'Human Papillomavirus Vaccine - 1st Dose^',
	),

	'FHS_HP_2' => array(
		'tc' => '人類乳頭瘤病毒疫苗 - 第2次^',
		'en' => 'Human Papillomavirus Vaccine - 2nd Dose^',
	),

	'FHS_MMR_1' => array(
		'tc' => '麻疹、流行性腮腺炎及德國麻疹混合疫苗 - 第1次',
		'en' => 'Measles, Mumps & Rubella (MMR) Vaccine - 1st Dose',
	),

	'FHS_MMR_2' => array(
		'tc' => '麻疹、流行性腮腺炎、德國麻疹及水痘混合疫苗 - 第2次*',
		'en' => 'Measles, Mumps, Rubella & Varicella (MMRV) Vaccine - 2nd Dose*',
	),

	'FHS_PV_1' => array(
		'tc' => '肺炎球菌疫苗 - 第1次',
		'en' => 'Pneumococcal Vaccine - 1st Dose',
	),

	'FHS_PV_2' => array(
		'tc' => '肺炎球菌疫苗 - 第2次',
		'en' => 'Pneumococcal Vaccine - 2nd Dose',
	),

	'FHS_PV_BD' => array(
		'tc' => '肺炎球菌疫苗 - 加強劑',
		'en' => 'Pneumococcal Vaccine - Booster Dose',
	),

	'FHS_VAR_1' => array(
		'tc' => '水痘疫苗 - 第1次',
		'en' => 'Varicella Vaccine - 1st Dose',
	),

	'/' => array(
		'tc' => '/',
		'en' => '',
	)
);

$ageAAr = array('m00' => ['tc' => '初生', 'en' => 'Newborn', 'dur' => 0], 
				'm01' => ['tc' => '1個月', 'en' => '1 month', 'dur' => 30], 
				'm02' => ['tc' => '6 - 8星期', 'en' => '6 - 8 weeks', 'dur' => 42],
				'm03' => ['tc' => '3個月', 'en' => '3 months', 'dur' => 91], 
				'm04' => ['tc' => '4個月', 'en' => '4 months', 'dur' => 122], 
				'm05' => ['tc' => '5個月', 'en' => '5 months', 'dur' => 152], 
				'm06' => ['tc' => '6個月', 'en' => '6 months', 'dur' => 183], 
				'm07' => ['tc' => '7個月', 'en' => '7 months', 'dur' => 213], 
				'm08' => ['tc' => '8個月', 'en' => '8 months', 'dur' => 244], 
				'm09' => ['tc' => '9個月', 'en' => '9 months', 'dur' => 274], 
				'm10' => ['tc' => '10個月', 'en' => '10 months', 'dur' => 305], 
				'm11' => ['tc' => '11個月', 'en' => '11 months', 'dur' => 335], 
				'm12' => ['tc' => '1歲', 'en' => '1 year', 'dur' => 365], 
				'm13' => ['tc' => '13個月', 'en' => '13 months', 'dur' => 395], 
				'm14' => ['tc' => '14個月', 'en' => '14 months', 'dur' => 426], 
				'm15' => ['tc' => '15個月', 'en' => '15 months', 'dur' => 456], 
				'm16' => ['tc' => '16個月', 'en' => '16 months', 'dur' => 487], 
				'm17' => ['tc' => '17個月', 'en' => '17 months', 'dur' => 517], 
				'm18' => ['tc' => '18個月', 'en' => '18 months', 'dur' => 548], 
				'm19' => ['tc' => '19個月', 'en' => '19 months', 'dur' => 578], 
				'm20' => ['tc' => '20個月', 'en' => '20 months', 'dur' => 609], 
				'm21' => ['tc' => '21個月', 'en' => '21 months', 'dur' => 639], 
				'm22' => ['tc' => '22個月', 'en' => '22 months', 'dur' => 670], 
				'm23' => ['tc' => '23個月', 'en' => '23 months', 'dur' => 700], 
				'm24' => ['tc' => '2歲', 'en' => '2 years', 'dur' => 730], 
				'p1' => ['tc' => '小一', 'en' => 'Primary 1', 'dur' => 2190], 
				'p2' => ['tc' => '小二', 'en' => 'Primary 2', 'dur' => 2555], 
				'p3' => ['tc' => '小三', 'en' => 'Primary 3', 'dur' => 2920], 
				'p4' => ['tc' => '小四', 'en' => 'Primary 4', 'dur' => 3285], 
				'p5' => ['tc' => '小五', 'en' => 'Primary 5', 'dur' => 3650], 
				'p6' => ['tc' => '小六', 'en' => 'Primary 6', 'dur' => 4015] 
);


/**
 * Convert the age codes and vaccine IDs to TC and EN.
 * Return the program timetable 
 */
function create_timetable(array $vaccDateAAr, $dr_tcname, $dr_enname, $ageAAr, $birthday, $vacc_termAAr) {
	$html = "<table class='cu-vacc-table2'>";

	$html .= "<thead><tr class='cu-tb2-header'>
				<td class='cu-tb2-date'>建議接種日期<br>Suggested Vaccination Date</td>
				<td class='cu-tb2-age'>年歲/年級<br>Age/Grade</td>
				<td class='cu-tb2-drname'>$dr_tcname<br>$dr_enname</td>
			  </tr></thead>";

	foreach ($vaccDateAAr as $age => $vArr) {
		
		$html .= "<tr class='cu-tb2-row'>";

		$html .= "<td class='cu-tb2-col-date'>". get_vacc_date($ageAAr, $age, $birthday). "</td>";

		$html .= "<td class='cu-tb2-col-age'>". get_age_terms($ageAAr, $age). "</td>";

		//cu_print_r($vArr);


		$html .= "<td class='cu-tb2-col-vacc'> <ul class='cu-tb2-vacc-tc'>";

		// Get T. Chinese terms
		foreach ($vArr as $v) {
			$html .= get_vacc_tc($v, $vacc_termAAr);
		}

		$html .= "</ul><ul class='cu-tb2-vacc-en'>";

		// Get English terms
		foreach ($vArr as $v) {
			$html .= get_vacc_en($v, $vacc_termAAr);
		}

		$html .= "</ul></td>";
			
		$html .= "</tr>";			

	}  // foreach($tableArr)

	$html .= "</table>";

	return $html;

} //create_timetable()



function get_vacc_tc($v, $vacc_termAAr) {
	return "<li>". $vacc_termAAr[$v]['tc']. "</li>";
	
} // get_vacc_tc()


function get_vacc_en($v, $vacc_termAAr) {
	return "<li>". $vacc_termAAr[$v]['en']. "</li>";
	
} // get_vacc_en()


function get_age_terms($ageAAr, $age) {
	return "<div>". $ageAAr[$age]['tc']. "</div><div style='line-height:1.1; padding-top:5px;'>". $ageAAr[$age]['en']. "</div>";

} //get_age_terms()



function get_vacc_date($ageAAr, $age, $birthday) {
	$duration = $ageAAr[$age]['dur'];
	$vacc_date = date('Y/m/d', strtotime($birthday. ' + '. $duration.' days'));
	if ($age == 'm00') $vacc_date = '(已於出世時接種)<br>(Vaccinated at birth)';

	if ($age == 'm02') {
		$week_8 = date('Y/m/d', strtotime($vacc_date. '+ 14 days'));
		$vacc_date = $vacc_date. ' - '. $week_8;
	}

	return $vacc_date;

} // get_vacc_date



/**
 * Clean the _POST array and create the SQL Where clause 
 * 
 * The arguement is the $_POST array. 
 * 
 * Called by :
 * > page-vaccine-submitted-records.php
 * > page-vaccine-submitted-csv-download.php
 * 
*/
function clean_create_sql_where(array $POST_Arr) {

	$postArr = array();
	if (! empty($POST_Arr)) {
		foreach($POST_Arr as $k => $v) {
			$postArr[$k] = sanitize_text_field($v);
		}
	}	

	array_pop($postArr);

	$where = "WHERE ";
	$whereArr = array();
	foreach ($postArr as $col => $value) {
		if ($value != '') $whereArr[] = "$col LIKE '%%$value%%'";
	}

	$where .= join(' AND ', $whereArr);
	$where = ($where == 'WHERE ')? '' : $where;
	return $where;

} // clean_create_sql_where()


/**
 * Control number of temp files in directory not to exceed $max_files
 * The oldest files are removed first.
 * 
 * Input arguments:
 * 	- relative directory path
 * 	- $max_files
 * 
 * Called by:
 * 	- page-vaccine-program-display.php
 * 	- page-vaccine-submitted-csv-download.php 
 * 
*/
function control_temp_files($rel_path, $max_files) {
	$tmp_file_dir = get_stylesheet_directory(). "/vaccine-program/$rel_path/";
	$tmp_fileArr = array_values(array_diff(scandir($tmp_file_dir), array('..', '.')));

	$total_files = sizeof($tmp_fileArr);
	$files_delete = $total_files - $max_files;

	for ($i = 0; $i < $files_delete; $i++) {
		unlink($tmp_file_dir.$tmp_fileArr[$i]);
	}

}  // control_temp_files()


/**
 * Helper function to display variable content
 * using print_r().
 * Use for debugging of codes.
*/
function cu_print_r($var) {
	echo "<pre>";
	print_r($var);
	echo "</pre>";

} // my_print_r()