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>
<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> -
©<?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