CSCI 2006 - Spring 2024 - Server-Side ProgrammingLab #8 - DBMS MODIFICATIONSolution

Solution

index.php

<?php

date_default_timezone_set('America/Chicago');
$date_string = date('Y-m-d H:i');

require('language.php');

$title = '404 Error';
$body = '<h2>404 Error</h2><p>The requested page was not available</p>';

if (!isset($_GET['pg'])) { 
	$title = 'Programming Languages';
	$_GET['pg'] = 'c'; 
}

if ($_GET['pg'] == 'edit') {
	/* We need to edit a language */
	$title = 'Language Editor';
	$lang = new Language($_GET['id']??null);
	$editor = $lang->getEditor();
	$body = <<<__HTML__
	<style>
	table {  
	   width: 90%;
	   margin: 0 auto;
	}
	table tbody td{
	   vertical-align: top;
	   text-align: left;
	}
	legend {
	   background-color: #616161 ;
	    color: white;
	   margin: 0 auto;
	   width: 90%;
	   padding: 0.25em;
	   text-align: center;
	   font-weight: bold;
	   font-size: 24px;
	}
	fieldset {
	   margin: 1em auto;
	   width: 70%;
	}
	form p {
	   margin-top: 0.5em;
	}
	form input, form select {
	    font-size: 16px;
		height: 24px;
		padding: 3px;
	}
	form select {
	    height: 30px;
	}
	form textarea {
	    height: 5em;
	}
	.remove { margin: 5px; }

	#name, #example { width: 95%; }
	#slug, #hi { width: 90%; }
	.desc { width: 90%; }
	.ord { margin: 5px; width: 50px; }
	</style>
	<script type="text/javascript">
	document.addEventListener("DOMContentLoaded", function () {
		var ord = +document.querySelector("#paragraph").dataset.lastord;
		document.querySelector("#addParagraph input").addEventListener("click",(e) => {
			ord++;
			var template = document.querySelector("#paragraph").content.cloneNode(true);
			template.querySelector(".ord").setAttribute("value", ord);
			var ref = document.querySelector("#addParagraph");
			ref.parentElement.insertBefore(template,ref);
		});
		document.querySelector("form").addEventListener("click",(e) => {
			if (e.target.classList.contains("remove")) {
				e.target.closest("tr").remove();
			}
		});
	});
	</script>
	<form method="POST" action="?pg=form">
		<fieldset>
			<legend>Language Editor</legend>
			{$editor}
			<button type="submit" name="action" value="save">Save</button>&nbsp;
			<button type="submit" name="action" value="cancel">Cancel</button>
		</fieldset>
	</form>
__HTML__;
} else if ($_GET['pg'] == 'form') {
	$lang = Language::processForm($_POST['lang']);
	$tital = $lang->getName();
	$body = $lang->toHTML();
	$body .= '<p><a href="?pg=edit&id='.$lang->getId().'">Edit this Language</a></p>';
} else {
	/* We need to display a language */
	$lang = new Language($_GET['pg']);
	if ($lang->isDefined()) {
		$title = $lang->getName();
		$body = $lang->toHTML();
		$body .= '<p><a href="?pg=edit&id='.$_GET['pg'].'">Edit this Language</a></p>';
	}
}

$allLangs = Language::getAllLanguages();
$nav = '';
foreach ($allLangs as $key=>$disp) {
	$nav .= '<a href="?pg='.$key.'" class="'.(($_GET['pg']==$key)?'currentPage':'').'">'.$disp.'</a>';
}

?>
<!DOCTYPE html>
<html>
<head>
  <title><?php echo $title; ?></title>
  <link rel="stylesheet" href="/style.css" type="text/css" media="all">
</head>
<body>
  <header>
    <h1>Programming Languages</h1>
    <nav><?php echo $nav; ?></nav>
  </header>
  <main>
  <?php echo $body; ?>
  </main>
  <script type="text/javascript" src="/theme/prism.js"></script>
  <footer>
    <a href="?pg=edit&id=new">New Language</a> - 
    &copy;<?php echo $date_string; ?> - General Websites Inc.
  </footer>
</body>
</html>

language.php

<?php

function getData($query,$args=[],$isModify=false,$isInsert=false) {
        $DB = 'csci2006_languages';
        $USER = 'csci2006';
        $PASS = '9TfP4lxsxH1szKuA';

        try {
                $pdo = new PDO("mysql:host=localhost;dbname={$DB}",$USER,$PASS);
                $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

		$rs = $pdo->prepare($query,[PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
		$rs->setFetchMode(PDO::FETCH_NAMED);
		$rs->execute($args);

		unset($DB);
		unset($USER);
		unset($PASS);
		if ($isInsert) {
			$id = $pdo->lastInsertId();
			
			unset($rs);
			unset($pdo);
			
			return $id;
		}

		if (!$isModify) {
			$data = [];
			while ($row = $rs->fetch()) {
				$data[] = $row;
			}
	
			unset($rs);
			unset($pdo);

			if (count($data) == 1 && isset($data[0])) {
				return $data[0];
			}
			return $data;
		}

		unset($rs);
		unset($pdo);

		return null;
	} catch (PDOException $e) {
		echo '<h2>Database Error</h2>';
		echo '<pre>'.$e->getMessage().'</pre>';
		echo '<pre>QUERY: '.$query.'</pre>';
		echo '<pre>ARGS: '.var_export($args,true).'</pre>';
		echo '<pre>Backtrace: '.var_export(debug_backtrace(),true).'</pre>';
                die();
        }
}

class Language {
	private $data = [];

	public function __construct($key) {
		$this->data = getData('SELECT *'.
			' FROM `language`'.
			' WHERE `language_slug`=?',
			[$key]);
		if (count($this->data) > 0) {
			$this->data['desc'] = getData('SELECT `desc_paragraph`'.
				' FROM `description`'.
				' WHERE `desc_lang`=?'.
				' ORDER BY `desc_ordinal`',
				[$this->data['lang_id']]);
		} else {
			$this->data = null;
		}
	}

	public function isDefined() {
		return ($this->data !== null); 
	}

	public function getId() {
		return $this->data['lang_id'];
	}

	public function getName() {
		return $this->data['language_name'];
	}

	public function getEditor() {
		$html = '<table>';

		$vals = (($this->isDefined())?$this->data:[
			'lang_id'=>'new',
			'langauge_slug'=>'',
			'language_name'=>'',
			'language_example'=>'',
			'language_highlighter'=>'',
			'desc'=>[],
		]);

		/* Name */
		$html .= '<tr>'.
			'<td colspan="2"><p>'.
			'<input type="hidden" name="lang[id]" value="'.$vals['lang_id'].'">'.
			'<label for="name">Name</label>'.
			'<input type="text" id="name" name="lang[name]" value="'.htmlentities($vals['language_name']).'">'.
			'</p></td>'.
			'</tr>';


		/* Slug & Highlight */
		$html .= '<tr>'.
			'<td><p>'.
			'<label for="slug">URL Slug</label>'.
			'<input type="text" id="slug" name="lang[slug]" value="'.htmlentities($vals['language_slug']).'">'.
			'</p></td>'.
			'<td><p>'.
			'<label for="hi">Highlight Code</label>'.
			'<input type="text" id="hi" name="lang[hi]" value="'.htmlentities($vals['language_highlighter']).'">'.
			'</p></td>'.
			'</tr>';

		/* Example */
		$html .= '<tr>'.
			'<td colspan="2"><p>'.
			'<label for="example">Example</label>'.
			'<textarea id="example" name="lang[example]">'.htmlentities($vals['language_example']).'</textarea>'.
			'</p></td>'.
			'</tr>';

		/* Description */
		$html .= '<tr><td colspan="2"><h3>Description</h3></td></tr>';
		$c = 0;
		foreach ($vals['desc'] as $d) {
			$html .= '<tr>'.
				'<td><p>'.
				'<textarea class="desc" name="lang[desc][text][]">'.htmlentities($d['desc_paragraph']).'</textarea>'.
				'</p></td>'.
				'<td><p>'.
				'<label>Ordinal</label>'.
				'<input type="number" class="ord" name="lang[desc][ord][]" value="'.$c.'">'.
				'<span class="remove">X</span>'.
				'</p></td>'.
				'</tr>';
			$c++;
		}
		$html .= '<tr id="addParagraph">'.
			'<td colspan="2"><p>'.
			'<input type="button" class="btn" value="Add Paragraph">'.
			'</td>'.
			'</tr>';


		$html .= '</table>'.
			'<template id="paragraph" data-lastord="'.$c.'">'.
				'<tr>'.
				'<td><p>'.
				'<textarea class="desc" name="lang[desc][text][]"></textarea>'.
				'</p></td>'.
				'<td><p>'.
				'<label>Ordinal</label>'.
				'<input class="ord" type="number" name="lang[desc][ord][]" value="">'.
				'<span class="remove">X</span>'.
				'</p></td>'.
				'</tr>'.
			'</template>';
		
		return $html;
	}

	public static function processForm($data) {
		$isNew = ($data['id']=='new');
		$langData = [
			$data['slug'],
			$data['name'],
			$data['example'],
			$data['hi'],
		];

		$lang_id = $data['id'];
		/* Handle the changes to the language itself */
		if ($isNew) {
			$lang_id = getData('INSERT INTO `language`'.
				' (`language_slug`,`language_name`,`language_example`,`language_highlighter`)'.
				' VALUES (?, ?, ?, ?)',
				$langData, true, true);
		} else {
			$langData[] = $lang_id;
			getData('UPDATE `language`'.
				' SET `language_slug`=?,'.
				' `language_name`=?,'.
				' `language_example`=?,'.
				' `language_highlighter`=?'.
				' WHERE `lang_id`=?',
				$langData, true);
		}

		/* Start preparing the return value */
		$l = new Language($lang_id);
		$l->data = [
			'language_slug'=>$data['slug'],
			'language_name'=>$data['name'],
			'language_example'=>$data['example'],
			'language_highlighter'=>$data['hi'],
			'desc'=>[],
		];

		/* Handle changes to the description paragraphs */
		$paras = getData('SELECT `desc_ordinal`,`desc_paragraph`'.
				' FROM `description`'.
				' WHERE `desc_lang`=?'.
				' ORDER BY `desc_ordinal`',
				[$lang_id]);
		$curParas = [];
		foreach ($paras as $p) {
			$curParas[intval($p['desc_ordinal'])] = $p['desc_paragraph'];
		}

		$newParas = [];
		foreach ($data['desc']['text'] as $k=>$text) {
			$ord = intval($data['ord'][$k]);
			while (isset($newParas[$ord])) {
				$ord += 0.000001;
			}
			$newParas[$ord]['desc_paragraph'] = $text; 
		}
		ksort($newParas);
		$l->data['desc'] = $newParas;

		foreach ($newParas as $ord=>$text) {
			if (isset($curParas[$ord])) {
				/* Existing Paragraph Ordinal */
				getData('UPDATE `description`'.
					' SET `desc_paragraph`=?'.
					' WHERE `desc_lang`=?'.
					'   AND `desc_ordinal`=?',
					[$text['desc_paragraph'],$lang_id,$ord], true);
				unset($curParas[$ord]);
			} else {
				/* New Paragraph Ordinal */
				getData('INSERT INTO `description`'.
					' (`desc_lang`,`desc_ordinal`,`desc_paragraph`)'.
					' VALUES (?, ?, ?)',
					[$lang_id, $ord, $text['desc_paragraph']], true);
			}
		}
		foreach ($curParas as $ord=>$text) {
			/* Remove the extra paragraphs */
			getData('DELETE FROM `description`'.
				' WHERE `desc_lang`=?'.
				'   AND `desc_ordinal`=?',
				[$lang_id, $ord], true);
		}

		return $l;
	}

	public function toHTML() {
		$html = '';
		foreach ($this->data['desc'] as $line) {
			$html .= '<p>'.$line['desc_paragraph'].'</p>';
		}
		$html .= '<h2>Example</h2>'.
			'<pre class="language-'.$this->data['language_highlighter'].' line-numbers"><code class="language-'.$this->data['language_highlighter'].'">'.
			htmlentities($this->data['language_example']).
			'</code></pre>';
		return $html;
	}

	public static function getAllLanguages() {
		$data = getData('SELECT `language_slug`, `language_name`'.
			' FROM `language`'.
			' ORDER BY `language_name`');
		$rs = [];
		foreach ($data as $info) {
			$rs[$info['language_slug']] = $info['language_name'];
		}
		return $rs;
	}
}

?>
Go To Live Site