diff --git a/config/bootstrap.php b/config/bootstrap.php
index 7da3234..06da29f 100644
--- a/config/bootstrap.php
+++ b/config/bootstrap.php
@@ -158,14 +158,14 @@ function create_tables(): void {
// css table
$db->exec("CREATE TABLE IF NOT EXISTS css (
id INTEGER PRIMARY KEY,
- filename TEXT NOT NULL,
+ filename TEXT UNIQUE NOT NULL,
description TEXT NULL
)");
// mood table
$db->exec("CREATE TABLE IF NOT EXISTS mood (
id INTEGER PRIMARY KEY,
- emoji TEXT NOT NULL,
+ emoji TEXT UNIQUE NOT NULL,
description TEXT NOT NULL
)");
} catch (PDOException $e) {
diff --git a/public/css/tkr.css b/public/css/tkr.css
index 2db66fc..aad05fc 100644
--- a/public/css/tkr.css
+++ b/public/css/tkr.css
@@ -304,6 +304,58 @@ label.description {
outline: 2px solid var(--color-emoji-border);
}
+.emoji-checkbox-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 12px;
+ border: 1px solid var(--color-border-light);
+ border-radius: 6px;
+ background-color: var(--color-bg-white);
+ transition: all 0.2s ease;
+ margin-bottom: 6px;
+}
+
+.emoji-checkbox-item:hover {
+ background-color: var(--color-hover-light);
+ border-color: var(--color-primary);
+}
+
+.emoji-checkbox-item input[type="checkbox"] {
+ width: auto;
+ margin: 0;
+ cursor: pointer;
+}
+
+.emoji-checkbox-item label {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin: 0;
+ padding: 0;
+ font-weight: 400;
+ cursor: pointer;
+ flex-grow: 1;
+ text-align: left;
+}
+
+.emoji-display {
+ font-size: 1.8em;
+ min-width: 2em;
+ text-align: center;
+}
+
+.emoji-description {
+ flex-grow: 1;
+ color: var(--color-text-primary);
+ font-size: 1em;
+}
+
+.emoji-checkbox-item input[type="checkbox"]:focus {
+ outline: none;
+ box-shadow: 0 0 0 2px var(--shadow-primary);
+}
+
/*
Responsive layout - adjusts from 1 to 2 columns based on screen width
- min-width makes the mobile (stacked) view the default
@@ -338,10 +390,4 @@ label.description {
.file-info {
grid-column: 2;
}
-
- .form-row .fieldset-items {
- grid-template-columns: 200px 1fr;
- gap: 16px;
- margin-bottom: 16px;
- }
}
\ No newline at end of file
diff --git a/src/Controller/CssController/CssController.php b/src/Controller/CssController/CssController.php
index 9209a3e..514ee9f 100644
--- a/src/Controller/CssController/CssController.php
+++ b/src/Controller/CssController/CssController.php
@@ -56,10 +56,10 @@ class CssController extends Controller {
$this->handleUpload();
break;
case 'set_theme':
- $this->handleSetTheme($config);
+ $this->handleSetTheme();
break;
case 'delete':
- $this->handleDelete($config);
+ $this->handleDelete();
break;
}
@@ -67,7 +67,9 @@ class CssController extends Controller {
exit;
}
- public function handleDelete(ConfigModel $config): void{
+ public function handleDelete(): void{
+ global $config;
+
// Don't try to delete the default theme.
if (!$_POST['selectCssFile']){
http_response_code(400);
@@ -114,7 +116,9 @@ class CssController extends Controller {
$config = $config->save();
}
- private function handleSetTheme(ConfigModel $config) {
+ private function handleSetTheme() {
+ global $config;
+
if ($_POST['selectCssFile']){
// Set custom theme
$config->cssId = $_POST['selectCssFile'];
diff --git a/src/Controller/MoodController/MoodController.php b/src/Controller/MoodController/MoodController.php
index be99f2b..422f9d2 100644
--- a/src/Controller/MoodController/MoodController.php
+++ b/src/Controller/MoodController/MoodController.php
@@ -5,7 +5,7 @@
global $user;
$view = new MoodView();
- $moodPicker = $view->render_mood_picker(self::get_emojis_with_labels(), $user->mood);
+ $moodPicker = $view->render_mood_picker(self::getEmojisWithLabels(), $user->mood);
$vars = [
'config' => $config,
@@ -15,7 +15,76 @@
$this->render("mood.php", $vars);
}
- public function handleMood(){
+ // Shows the custom emoji management page
+ public function showCustomEmoji(){
+ global $config;
+ $emojiList = MoodModel::loadAll();
+
+ $vars = [
+ 'config' => $config,
+ 'emojiList' => $emojiList,
+ ];
+
+ $this->render("emoji.php", $vars);
+ }
+
+ public function handlePost(): void {
+ global $config;
+
+ switch ($_POST['action']) {
+ case 'add':
+ $emoji = trim($_POST['emoji']);
+ $description = trim($_POST['emoji-description']);
+ $this->handleAdd($emoji, $description);
+ break;
+ case 'delete':
+ if (!empty($_POST['delete_emoji_ids'])){
+ $this->handleDelete();
+ }
+ break;
+ }
+
+ header('Location: ' . $config->basePath . 'admin/emoji');
+ exit;
+ }
+
+ public function handleAdd(string $emoji, ?string $description=null): void {
+ // Validate 1 visible character in the emoji
+ $charCount = mb_strlen($emoji, 'UTF-8');
+ if ($charCount !== 1) {
+ // TODO - handle error
+ return;
+ }
+
+ // Validate the emoji is actually an emoji
+ $emojiPattern = '/^[\x{1F000}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{1F900}-\x{1F9FF}\x{1FA70}-\x{1FAFF}]$/u';
+
+ if (!preg_match($emojiPattern, $emoji)) {
+ // TODO - handle error
+ return;
+ }
+
+ // emojis should have more bytes than characters
+ $byteCount = strlen($emoji);
+ if ($byteCount <= 1) {
+ // TODO - handle error
+ return;
+ }
+
+ // It looks like an emoji. Let's add it.
+ MoodModel::add($emoji, $description);
+ }
+
+ public function handleDelete(): void {
+ $ids = $_POST['delete_emoji_ids'];
+
+ if (!empty($ids)) {
+ $moodModel = new MoodModel();
+ $moodModel->delete($ids);
+ }
+ }
+
+ public function handleSetMood(){
if ($_SERVER['REQUEST_METHOD'] === 'POST' and isset($_POST['mood'])) {
// ensure that the session is valid before proceeding
if (!Session::validateCsrfToken($_POST['csrf_token'])) {
@@ -37,8 +106,18 @@
}
}
- private static function get_emojis_with_labels(): array {
- return [
+ private static function getEmojisWithLabels(): array {
+ $customEmoji = MoodModel::loadAll();
+
+ if (!empty($customEmoji)){
+ $custom = [];
+
+ foreach ($customEmoji as $item){
+ $custom[] = [$item['emoji'], $item['description']];
+ }
+ }
+
+ $emoji = [
'faces' => [
['😀', 'grinning face'],
['😄', 'grinning face with smiling eyes'],
@@ -239,9 +318,14 @@
['🛴', 'kick scooter'],
['⛵', 'sailboat'],
],
-
- //'custom' => get_user_emojis($db),
];
+
+ // add custom emoji if there are any
+ if (isset($custom)){
+ $emoji = ['custom' => $custom] + $emoji;
+ }
+
+ return $emoji;
}
}
?>
\ No newline at end of file
diff --git a/src/Framework/Router/Router.php b/src/Framework/Router/Router.php
index 4365197..10dea0f 100644
--- a/src/Framework/Router/Router.php
+++ b/src/Framework/Router/Router.php
@@ -10,13 +10,15 @@ class Router {
['admin', 'AdminController@handleSave', ['POST']],
['admin/css', 'CssController'],
['admin/css', 'CssController@handlePost', ['POST']],
+ ['admin/emoji', 'MoodController@showCustomEmoji'],
+ ['admin/emoji', 'MoodController@handlePost', ['POST']],
['feed/rss', 'FeedController@rss'],
['feed/atom', 'FeedController@atom'],
['login', 'AuthController@showLogin'],
['login', 'AuthController@handleLogin', ['POST']],
['logout', 'AuthController@handleLogout', ['GET', 'POST']],
['mood', 'MoodController'],
- ['mood', 'MoodController@handleMood', ['POST']],
+ ['mood', 'MoodController@handleSetMood', ['POST']],
['setup', 'AdminController@showSetup'],
['setup', 'AdminController@handleSetup', ['POST']],
['tick/{y}/{m}/{d}/{h}/{i}/{s}', 'TickController'],
diff --git a/src/Model/MoodModel/MoodModel.php b/src/Model/MoodModel/MoodModel.php
new file mode 100644
index 0000000..40eb0f0
--- /dev/null
+++ b/src/Model/MoodModel/MoodModel.php
@@ -0,0 +1,29 @@
+query("SELECT id, emoji, description FROM mood");
+ return $stmt->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ // I'm not going to support editing emoji.
+ // It'll just be a delete/readd
+ public static function add(string $emoji, ?string $description): void{
+ global $db;
+
+ $stmt = $db->prepare("INSERT INTO mood (emoji, description) VALUES (?, ?)");
+ $stmt->execute([$emoji, $description]);
+ }
+
+ public static function delete(array $idsToDelete): void{
+ global $db;
+
+ $placeholders = rtrim(str_repeat('?,', count($idsToDelete)), ',');
+ $stmt = $db->prepare("DELETE FROM mood WHERE id IN ($placeholders)");
+ $stmt->execute($idsToDelete);
+ }
+}
\ No newline at end of file
diff --git a/templates/feed/rss.php b/templates/feed/rss.php
index 79a6ff3..f902d53 100644
--- a/templates/feed/rss.php
+++ b/templates/feed/rss.php
@@ -16,7 +16,7 @@ echo '' . "\n";
href="baseUrl . $config->basePath)?>feed/rss/" />
baseUrl . $config->basePath) ?> />
+ href="baseUrl . $config->basePath) ?>" />