diff --git a/public/admin/deleteMessage.php b/public/admin/deleteMessage.php
new file mode 100755
index 0000000..e5b65f4
--- /dev/null
+++ b/public/admin/deleteMessage.php
@@ -0,0 +1,37 @@
+prepare("DELETE FROM messages WHERE id = ?");
+if (!$query) {
+ http_response_code(500);
+ die("Error: {$conn->error}");
+}
+if (!$query->bind_param("i", $id)) {
+ http_response_code(400);
+ die("Error: message ID must be an integer.");
+}
+if (!$query->execute()) {
+ http_response_code(500);
+ die("Error {$query->errno}: {$query->error}");
+}
+
+if ($query->affected_rows === 0) {
+ http_response_code(400);
+ die("Error: that message does not exist");
+} else {
+ die("Removed message $id successfully.");
+}
diff --git a/public/admin/index.php b/public/admin/index.php
index 91d5d87..9a47a40 100755
--- a/public/admin/index.php
+++ b/public/admin/index.php
@@ -12,60 +12,156 @@ $conn = new mysqli("localhost", "mileslinden", "Daiso@6969", "mileslinden");
$result = $conn->query("SELECT * FROM subscribers");
if (!$result) {
http_response_code(500);
- die("Error: {$conn->error}");
+ die("Error retrieving subscribers: {$conn->error}");
}
$subscribers = [];
while ($row = $result->fetch_assoc()) {
$subscribers[] = $row;
}
+
+$result = $conn->query("SELECT id, full_name, subject, send_date FROM messages ORDER BY send_date DESC");
+if (!$result) {
+ http_response_code(500);
+ die("Error retrieving messages: {$conn->error}");
+}
+
+$messages = [];
+while ($row = $result->fetch_assoc()) {
+ $messages[] = $row;
+}
?>
Miles Linden for San Jose City Council
+
Admin Panel
-
-
Logout
-
Mail All
+
-
Subscribers
-
-
-
- Name |
- Email |
- Phone |
-
- Join Date |
- Actions |
-
-
-
+
+
+
Subscribers
+
+
+
+ Name |
+ Email |
+ Phone |
+
+ Join Date |
+ Actions |
+
+
+
-
- = htmlspecialchars($row['full_name']) ?> |
-
- = htmlspecialchars($row['email']) ?> |
- = htmlspecialchars($row['phone']) ?> |
-
- = htmlspecialchars($row['join_date']) ?> |
-
-
- |
-
+
+ = htmlspecialchars($row['full_name']) ?> |
+
+ = htmlspecialchars($row['email']) ?> |
+ = htmlspecialchars($row['phone']) ?> |
+
+ = htmlspecialchars($row['join_date']) ?> |
+
+
+ |
+
-
-
+
+
+
+
+
+
+
diff --git a/public/admin/message.php b/public/admin/message.php
new file mode 100755
index 0000000..64505be
--- /dev/null
+++ b/public/admin/message.php
@@ -0,0 +1,52 @@
+prepare("SELECT * FROM messages WHERE id = ?");
+if (!$query) {
+ http_response_code(500);
+ die("Error: {$conn->error}");
+}
+if (!$query->bind_param("i", $id)) {
+ http_response_code(400);
+ die("Message ID must be an integer.");
+}
+if (!$query->execute()) {
+ http_response_code(500);
+ die("Error {$query->errno}: {$query->error}");
+}
+
+$result = $query->get_result();
+if ($result->num_rows === 0) {
+ http_response_code(404);
+ die("Message with ID $id not found.");
+}
+
+$message = $result->fetch_assoc();
+?>
+
+
+
+
+ Miles Linden for San Jose City Council
+
+
+ = isset($message['subject']) ? htmlspecialchars($message['subject']) : '(no subject)' ?>
+ From: = htmlspecialchars($message['full_name']) ?> (= htmlspecialchars($message['email']) ?>)
+ Date: = $message['send_date'] ?>
+ = htmlspecialchars($message['message']) ?>
+
+
diff --git a/public/admin/unsubscribe.php b/public/admin/unsubscribe.php
index b1224b9..1751e80 100755
--- a/public/admin/unsubscribe.php
+++ b/public/admin/unsubscribe.php
@@ -30,6 +30,5 @@ if ($query->affected_rows === 0) {
http_response_code(400);
die("Error: that email address does not exist");
} else {
- header('Location: /admin');
- die;
+ die("Removed subscribed user $email successfully.");
}
diff --git a/public/contact.php b/public/contact.php
new file mode 100755
index 0000000..b6b65ae
--- /dev/null
+++ b/public/contact.php
@@ -0,0 +1,79 @@
+ 'email',
+ 'message' => 'Error: A valid email address is required.'
+ ]));
+}
+if (!isset($fname)) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ die(json_encode([
+ 'field' => 'full_name',
+ 'message' => 'Error: A first and last name are required.'
+ ]));
+}
+if (!isset($message)) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ die(json_encode([
+ 'field' => 'email',
+ 'message' => 'Error: A message is required.'
+ ]));
+}
+if (!isset($subject)) {
+ $subject = null;
+}
+$conn = new mysqli("localhost", "mileslinden", "Daiso@6969", "mileslinden");
+
+$query = $conn->prepare(
+ "INSERT INTO messages (`email`, `full_name`, `subject`, `message`, `send_date`) VALUES (?, ?, ?, ?, ?)"
+);
+if (!$query) {
+ http_response_code(500);
+ header('Content-Type: application/json');
+ die(json_encode(['message' => $conn->error]));
+}
+if (!isset($_SESSION['messages'])) {
+ $_SESSION['messages'] = 0;
+} else if ($_SESSION['messages'] >= 5) {
+ http_response_code(429);
+ header('Content-Type: application/json');
+ die(json_encode(['message' => 'You are sending messages too often.']));
+}
+$query->bind_param(
+ "sssss",
+ $email, $fname, $subject, $message,
+ date("Y-m-d H:i:s")
+);
+if (!$query->execute()) {
+ http_response_code(500);
+ header('Content-Type: application/json');
+ die(json_encode(['message' => $query->error, 'errno' => $query->errno]));
+}
+$_SESSION['messages']++;
+
+?>
+
+
+
+
+ Miles Linden for San Jose City Council
+
+
+ Your message was sent successfully.
+
+
diff --git a/public/css/main.css b/public/css/main.css
index 01c72bf..cb0c9e6 100755
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -94,13 +94,13 @@ nav a:hover {
padding: 1rem;
}
-.banner form input {
+.banner form input, .banner form textarea {
border-bottom: 0.25rem solid black;
width: 60%;
min-width: 250px;
}
-.banner form input.invalid {
+.banner form input.invalid, .banner form textarea.invalid {
border-bottom-color: red;
}
@@ -220,6 +220,14 @@ nav a:hover {
text-align: center;
}
+#contactForm div {
+ padding: 0.5rem;
+}
+
+#contactForm input {
+ font-size: 1rem;
+}
+
.read-more {
margin-top: 4rem;
text-align: center;
diff --git a/public/index.php b/public/index.php
index da0d649..3b6c00c 100755
--- a/public/index.php
+++ b/public/index.php
@@ -36,7 +36,7 @@
Join
-
For any further inquiries please email
-
contact@mileslinden.com
+
diff --git a/public/js/app.js b/public/js/app.js
index 09a58c7..cdc0121 100755
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -1,76 +1,118 @@
-document.addEventListener('DOMContentLoaded', function () {
-
- var fullName = document.querySelector('input[name="full_name"]');
- var email = document.querySelector('input[name="email"]');
- var phone = document.querySelector('input[name="phone"]');
- var submit = document.querySelector('button[type="submit"]');
+function registerFormValidators(formId) {
+ var form = document.getElementById(formId);
+ var fullName = form.querySelector('input[name="full_name"]');
+ var email = form.querySelector('input[name="email"]');
+ var phone = form.querySelector('input[name="phone"]');
+ var subject = form.querySelector('input[name="subject"]');
+ var message = form.querySelector('textarea[name="message"]');
+ var submit = form.querySelector('button[type="submit"]');
const emailRegex = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
const phoneRegex = /^(\+?0?1\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
const fullNameRegex = /[^\s.]{2,} [^\s.]{2,}/;
- fullName.addEventListener('input', function (e) {
- if (! fullName.value.toLowerCase().match(fullNameRegex)) {
- fullName.classList.add('invalid');
- } else {
- fullName.classList.remove('invalid');
- }
- });
-
- email.addEventListener('input', function (e) {
- if (! email.value.toLowerCase().match(emailRegex)) {
- email.classList.add('invalid');
- } else {
- email.classList.remove('invalid');
- }
- });
-
- phone.addEventListener('input', function (e) {
- if (! phone.value.match(phoneRegex)) {
- phone.classList.add('invalid');
- } else {
- phone.classList.remove('invalid');
- }
- });
-
- submit.addEventListener('click', function (e) {
- e.preventDefault();
-
- if (! fullName.value.toLowerCase().match(fullNameRegex)) {
- alert("Your full name appears invalid.");
- return;
- }
- if (! email.value.toLowerCase().match(emailRegex)) {
- alert("Your email address is invalid.");
- return;
- }
- if (! phone.value.match(phoneRegex)) {
- alert("Your phone number is invalid.");
- return;
- }
- var fd = new FormData();
- fd.append('full_name', fullName.value);
- fd.append('email', email.value);
- fd.append('phone', phone.value);
- var req = fetch('/subscribe.php', {
- method: 'POST',
- body: fd
- }).then(function (res) {
- if (!res.ok) {
- res.json().then(function (err) {
- if ('field' in err) {
- document.querySelector('input[name="'+ err.field + '"]').classList.add('invalid');
- }
- alert(err.message);
- });
+ if (fullName) {
+ fullName.addEventListener('input', function (e) {
+ if (! fullName.value.toLowerCase().match(fullNameRegex)) {
+ fullName.classList.add('invalid');
} else {
- alert("You have subscribed successfully.");
- fullName.value = "";
- email.value = "";
- phone.value = "";
+ fullName.classList.remove('invalid');
}
});
- });
+ }
+ if (email) {
+ email.addEventListener('input', function (e) {
+ if (! email.value.toLowerCase().match(emailRegex)) {
+ email.classList.add('invalid');
+ } else {
+ email.classList.remove('invalid');
+ }
+ });
+ }
+
+ if (phone) {
+ phone.addEventListener('input', function (e) {
+ if (! phone.value.match(phoneRegex)) {
+ phone.classList.add('invalid');
+ } else {
+ phone.classList.remove('invalid');
+ }
+ });
+ }
+
+ if (message) {
+ message.addEventListener('input', function (e) {
+ if (!message.value) {
+ message.classList.add('invalid');
+ } else {
+ message.classList.remove('invalid');
+ }
+ });
+ }
+
+ if (submit) {
+ submit.addEventListener('click', function (e) {
+ e.preventDefault();
+
+ if (fullName && !fullName.value.toLowerCase().match(fullNameRegex)) {
+ alert("Your full name appears invalid.");
+ return;
+ }
+ if (email && !email.value.toLowerCase().match(emailRegex)) {
+ alert("Your email address is invalid.");
+ return;
+ }
+ if (phone && !phone.value.match(phoneRegex)) {
+ alert("Your phone number is invalid.");
+ return;
+ }
+ if (message && !message.value) {
+ alert("Your message is invalid.");
+ return;
+ }
+ var fd = new FormData();
+ if (fullName)
+ fd.append('full_name', fullName.value);
+ if (email)
+ fd.append('email', email.value);
+ if (phone)
+ fd.append('phone', phone.value);
+ if (message)
+ fd.append('message', message.value);
+ if (subject)
+ fd.append('subject', subject.value);
+ var req = fetch(form.action, {
+ method: 'POST',
+ body: fd
+ }).then(function (res) {
+ if (!res.ok) {
+ res.json().then(function (err) {
+ if ('field' in err) {
+ form.querySelector('input[name="'+ err.field + '"]').classList.add('invalid');
+ }
+ alert(err.message);
+ });
+ } else {
+ alert("Submitted successfully.");
+ if (fullName)
+ fullName.value = "";
+ if (email)
+ email.value = "";
+ if (phone)
+ phone.value = "";
+ if (message)
+ message.value = "";
+ if (subject)
+ subject.value = "";
+ }
+ });
+ });
+ }
+}
+
+document.addEventListener('DOMContentLoaded', function () {
+ registerFormValidators('subscribeForm');
+ registerFormValidators('contactForm');
});
diff --git a/public/subscribe.php b/public/subscribe.php
index efde88e..0182fef 100755
--- a/public/subscribe.php
+++ b/public/subscribe.php
@@ -6,10 +6,18 @@ session_start();
$email = $_POST['email'];
$fname = $_POST['full_name'];
$phone = $_POST['phone'];
-$gender = isset($_POST['gender']) && $_POST['gender'];
+$gender = $_POST['gender'];
$pattern = '/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD';
+if (!isset($phone)) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ die(json_encode([
+ 'field' => 'phone',
+ 'message' => 'Error: A phone number is required.'
+ ]));
+}
//eliminate every char except 0-9
$phone_num = preg_replace("/[^0-9]/", '', $phone);
@@ -17,19 +25,35 @@ $phone_num = preg_replace("/[^0-9]/", '', $phone);
if (strlen($phone_num) == 11)
$phone_num = preg_replace("/^1/", '', $phone_num);
-if (!$email || preg_match($pattern, $email) !== 1 || !$fname || strlen($phone_num) !== 10) {
+if (!isset($email) || preg_match($pattern, $email) !== 1) {
http_response_code(400);
header('Content-Type: application/json');
die(json_encode([
'field' => 'email',
- 'message' => 'Error: An email address, first name, and last name are required.'
+ 'message' => 'Error: A valid email address is required.'
+ ]));
+}
+if (!isset($fname)) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ die(json_encode([
+ 'field' => 'full_name',
+ 'message' => 'Error: A first and last name are required.'
+ ]));
+}
+if (strlen($phone_num) !== 10) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ die(json_encode([
+ 'field' => 'phone',
+ 'message' => 'Error: This phone number is invalid.'
]));
}
if ($gender && $gender !== 'm' && $gender !== 'f') {
http_response_code(400);
header('Content-Type: application/json');
die(json_encode([
- 'field' => 'full_name',
+ 'field' => 'gender',
'message' => 'Error: An invalid gender was given.'
]));
}