From 7a276764e6a1ee3619d9d3488f6163215db75338 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Thu, 2 Sep 2021 16:34:25 +0000
Subject: [PATCH] feat(themes): add ViewThemes library to set views in root
 themes folder

app, admin, install and authentication views are now located in root themes/ folder
---
 app/Common.php                                | 32 ++++++
 app/Config/Autoload.php                       |  2 +
 app/Config/ViewComponents.php                 | 14 ++-
 app/Controllers/BaseController.php            | 13 +--
 app/Controllers/HomeController.php            |  1 +
 app/Libraries/View.php                        |  2 +-
 .../ViewComponents/ComponentRenderer.php      | 22 ++---
 .../ViewComponents/Config/ViewComponents.php  | 13 +--
 app/Libraries/ViewThemes/Config/Themes.php    | 24 +++++
 app/Libraries/ViewThemes/Theme.php            | 99 +++++++++++++++++++
 app/{View => Views}/Components/Button.php     |  2 +-
 .../Components/Forms/Label.php                |  2 +-
 .../Components/Forms/MarkdownEditor.php       |  2 +-
 .../Components/Forms/MultiSelect.php          |  2 +-
 .../Components/Forms/Select.php               |  2 +-
 .../Components/Forms/Toggler.php              |  2 +-
 .../Components/Forms/XMLEditor.php            |  2 +-
 app/{View => Views}/Components/Icon.php       |  2 +-
 app/Views/components/.gitkeep                 |  0
 modules/Admin/Controllers/BaseController.php  | 13 +--
 .../Controllers/ContributorController.php     |  8 +-
 .../Admin/Controllers/EpisodeController.php   | 18 ++--
 .../Controllers/EpisodePersonController.php   |  2 +-
 .../Admin/Controllers/FediverseController.php |  6 +-
 .../Admin/Controllers/MyAccountController.php |  4 +-
 modules/Admin/Controllers/PageController.php  |  8 +-
 .../Admin/Controllers/PersonController.php    |  8 +-
 .../Admin/Controllers/PodcastController.php   | 29 +++---
 .../Controllers/PodcastImportController.php   |  2 +-
 .../Controllers/PodcastPersonController.php   |  2 +-
 .../Controllers/PodcastPlatformController.php |  6 +-
 modules/Admin/Controllers/UserController.php  |  8 +-
 modules/Auth/Config/Auth.php                  | 14 +--
 modules/Auth/Controllers/AuthController.php   |  8 ++
 .../Install/Controllers/InstallController.php | 17 ++--
 phpstan.neon                                  |  2 +
 tailwind.config.js                            |  1 +
 .../Views => themes/cp_admin}/_layout.php     |  6 +-
 .../cp_admin}/_partials/_user_info.php        |  0
 .../Views => themes/cp_admin}/_sidebar.php    |  0
 .../cp_admin}/contributor/add.php             |  2 +-
 .../cp_admin}/contributor/edit.php            |  2 +-
 .../cp_admin}/contributor/list.php            |  2 +-
 .../cp_admin}/contributor/view.php            |  2 +-
 .../Views => themes/cp_admin}/dashboard.php   |  2 +-
 .../cp_admin}/episode/create.php              |  2 +-
 .../cp_admin}/episode/edit.php                |  2 +-
 .../cp_admin}/episode/embeddable_player.php   |  2 +-
 .../cp_admin}/episode/list.php                |  2 +-
 .../cp_admin}/episode/persons.php             |  2 +-
 .../cp_admin}/episode/publish.php             |  2 +-
 .../cp_admin}/episode/publish_edit.php        |  2 +-
 .../cp_admin}/episode/soundbites.php          |  2 +-
 .../cp_admin}/episode/unpublish.php           |  2 +-
 .../cp_admin}/episode/view.php                |  2 +-
 .../cp_admin}/fediverse/blocked_actors.php    |  2 +-
 .../cp_admin}/fediverse/blocked_domains.php   |  2 +-
 themes/cp_admin/manifest.json                 |  4 +
 .../cp_admin}/my_account/change_password.php  |  2 +-
 .../cp_admin}/my_account/view.php             |  4 +-
 .../Views => themes/cp_admin}/page/create.php |  2 +-
 .../Views => themes/cp_admin}/page/edit.php   |  2 +-
 .../Views => themes/cp_admin}/page/list.php   |  2 +-
 .../Views => themes/cp_admin}/page/view.php   |  2 +-
 .../cp_admin}/person/create.php               |  2 +-
 .../Views => themes/cp_admin}/person/edit.php |  2 +-
 .../Views => themes/cp_admin}/person/list.php |  2 +-
 .../Views => themes/cp_admin}/person/view.php |  2 +-
 .../cp_admin}/podcast/_sidebar.php            |  0
 .../cp_admin}/podcast/analytics/index.php     |  2 +-
 .../podcast/analytics/listening_time.php      |  2 +-
 .../cp_admin}/podcast/analytics/locations.php |  2 +-
 .../cp_admin}/podcast/analytics/players.php   |  2 +-
 .../podcast/analytics/time_periods.php        |  2 +-
 .../podcast/analytics/unique_listeners.php    |  2 +-
 .../cp_admin}/podcast/analytics/webpages.php  |  2 +-
 .../cp_admin}/podcast/create.php              |  2 +-
 .../cp_admin}/podcast/edit.php                |  4 +-
 .../cp_admin}/podcast/import.php              |  2 +-
 .../cp_admin}/podcast/latest_episodes.php     |  0
 .../cp_admin}/podcast/list.php                |  2 +-
 .../cp_admin}/podcast/persons.php             |  2 +-
 .../cp_admin}/podcast/platforms.php           |  2 +-
 .../cp_admin}/podcast/settings/dashboard.php  |  2 +-
 .../cp_admin}/podcast/view.php                |  4 +-
 .../Views => themes/cp_admin}/user/create.php |  2 +-
 .../Views => themes/cp_admin}/user/edit.php   |  2 +-
 .../Views => themes/cp_admin}/user/list.php   |  2 +-
 .../Views => themes/cp_admin}/user/view.php   |  4 +-
 {app/Views => themes/cp_app}/_layout.php      |  0
 {app/Views => themes/cp_app}/credits.php      |  0
 .../cp_app}/embeddable_player.php             |  0
 {app/Views => themes/cp_app}/home.php         |  0
 themes/cp_app/manifest.json                   |  4 +
 {app/Views => themes/cp_app}/page.php         |  0
 .../cp_app}/podcast/_layout.php               |  0
 .../cp_app}/podcast/_layout_authenticated.php |  0
 .../cp_app}/podcast/_partials/comment.php     |  0
 .../podcast/_partials/comment_actions.php     |  0
 .../comment_actions_authenticated.php         |  0
 .../_partials/comment_actions_from_post.php   |  0
 ...omment_actions_from_post_authenticated.php |  0
 .../_partials/comment_authenticated.php       |  0
 .../podcast/_partials/comment_card.php        |  0
 .../_partials/comment_card_authenticated.php  |  0
 .../podcast/_partials/comment_reply.php       |  0
 .../_partials/comment_reply_actions.php       |  0
 .../comment_reply_actions_authenticated.php   |  0
 .../_partials/comment_reply_authenticated.php |  0
 .../_partials/comment_with_replies.php        |  0
 .../comment_with_replies_authenticated.php    |  0
 .../podcast/_partials/episode_card.php        |  0
 .../_partials/episode_preview_card.php        |  0
 .../cp_app}/podcast/_partials/header.php      |  0
 .../cp_app}/podcast/_partials/post.php        |  0
 .../podcast/_partials/post_actions.php        |  0
 .../_partials/post_actions_authenticated.php  |  0
 .../podcast/_partials/post_authenticated.php  |  0
 .../podcast/_partials/post_with_replies.php   |  0
 .../post_with_replies_authenticated.php       |  0
 .../podcast/_partials/preview_card.php        |  0
 .../cp_app}/podcast/_partials/reblog.php      |  0
 .../_partials/reblog_authenticated.php        |  0
 .../cp_app}/podcast/_partials/reply.php       |  0
 .../podcast/_partials/reply_actions.php       |  0
 .../_partials/reply_actions_authenticated.php |  0
 .../podcast/_partials/reply_authenticated.php |  0
 .../cp_app}/podcast/_partials/sidebar.php     |  0
 .../cp_app}/podcast/activity.php              |  0
 .../podcast/activity_authenticated.php        |  0
 .../cp_app}/podcast/comment.php               |  0
 .../cp_app}/podcast/comment_authenticated.php |  0
 .../cp_app}/podcast/episode.php               |  0
 .../cp_app}/podcast/episode_authenticated.php |  0
 .../cp_app}/podcast/episodes.php              |  1 +
 .../podcast/episodes_authenticated.php        |  1 +
 .../cp_app}/podcast/follow.php                |  0
 {app/Views => themes/cp_app}/podcast/post.php |  0
 .../cp_app}/podcast/post_authenticated.php    |  0
 .../cp_app}/podcast/post_remote_action.php    |  0
 .../Auth/Views => themes/cp_auth}/_layout.php |  2 +-
 .../cp_auth}/emails/activation.php            |  0
 .../cp_auth}/emails/forgot.php                |  0
 .../Auth/Views => themes/cp_auth}/forgot.php  |  0
 .../Auth/Views => themes/cp_auth}/login.php   |  0
 themes/cp_auth/manifest.json                  |  4 +
 .../Views => themes/cp_auth}/register.php     |  0
 .../Auth/Views => themes/cp_auth}/reset.php   |  0
 .../Views => themes/cp_install}/_layout.php   |  2 +-
 .../cp_install}/cache_config.php              |  2 +-
 .../cp_install}/create_superadmin.php         |  2 +-
 .../cp_install}/database_config.php           |  2 +-
 .../cp_install}/instance_config.php           |  4 +-
 themes/cp_install/manifest.json               |  4 +
 .../cp_install}/manual_config.php             |  2 +-
 155 files changed, 356 insertions(+), 178 deletions(-)
 create mode 100644 app/Libraries/ViewThemes/Config/Themes.php
 create mode 100644 app/Libraries/ViewThemes/Theme.php
 rename app/{View => Views}/Components/Button.php (98%)
 rename app/{View => Views}/Components/Forms/Label.php (96%)
 rename app/{View => Views}/Components/Forms/MarkdownEditor.php (99%)
 rename app/{View => Views}/Components/Forms/MultiSelect.php (95%)
 rename app/{View => Views}/Components/Forms/Select.php (94%)
 rename app/{View => Views}/Components/Forms/Toggler.php (97%)
 rename app/{View => Views}/Components/Forms/XMLEditor.php (93%)
 rename app/{View => Views}/Components/Icon.php (94%)
 delete mode 100644 app/Views/components/.gitkeep
 rename {modules/Admin/Views => themes/cp_admin}/_layout.php (93%)
 rename {modules/Admin/Views => themes/cp_admin}/_partials/_user_info.php (100%)
 rename {modules/Admin/Views => themes/cp_admin}/_sidebar.php (100%)
 rename {modules/Admin/Views => themes/cp_admin}/contributor/add.php (95%)
 rename {modules/Admin/Views => themes/cp_admin}/contributor/edit.php (94%)
 rename {modules/Admin/Views => themes/cp_admin}/contributor/list.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/contributor/view.php (94%)
 rename {modules/Admin/Views => themes/cp_admin}/dashboard.php (85%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/create.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/edit.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/embeddable_player.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/list.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/persons.php (98%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/publish.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/publish_edit.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/soundbites.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/unpublish.php (96%)
 rename {modules/Admin/Views => themes/cp_admin}/episode/view.php (98%)
 rename {modules/Admin/Views => themes/cp_admin}/fediverse/blocked_actors.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/fediverse/blocked_domains.php (97%)
 create mode 100644 themes/cp_admin/manifest.json
 rename {modules/Admin/Views => themes/cp_admin}/my_account/change_password.php (95%)
 rename {modules/Admin/Views => themes/cp_admin}/my_account/view.php (65%)
 rename {modules/Admin/Views => themes/cp_admin}/page/create.php (96%)
 rename {modules/Admin/Views => themes/cp_admin}/page/edit.php (96%)
 rename {modules/Admin/Views => themes/cp_admin}/page/list.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/page/view.php (90%)
 rename {modules/Admin/Views => themes/cp_admin}/person/create.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/person/edit.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/person/list.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/person/view.php (94%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/_sidebar.php (100%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/index.php (95%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/listening_time.php (94%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/locations.php (96%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/players.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/time_periods.php (95%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/unique_listeners.php (94%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/analytics/webpages.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/create.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/edit.php (98%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/import.php (99%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/latest_episodes.php (100%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/list.php (97%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/persons.php (98%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/platforms.php (98%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/settings/dashboard.php (84%)
 rename {modules/Admin/Views => themes/cp_admin}/podcast/view.php (90%)
 rename {modules/Admin/Views => themes/cp_admin}/user/create.php (95%)
 rename {modules/Admin/Views => themes/cp_admin}/user/edit.php (94%)
 rename {modules/Admin/Views => themes/cp_admin}/user/list.php (98%)
 rename {modules/Admin/Views => themes/cp_admin}/user/view.php (58%)
 rename {app/Views => themes/cp_app}/_layout.php (100%)
 rename {app/Views => themes/cp_app}/credits.php (100%)
 rename {app/Views => themes/cp_app}/embeddable_player.php (100%)
 rename {app/Views => themes/cp_app}/home.php (100%)
 create mode 100644 themes/cp_app/manifest.json
 rename {app/Views => themes/cp_app}/page.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_layout.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_layout_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_actions.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_actions_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_actions_from_post.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_actions_from_post_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_card.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_card_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_reply.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_reply_actions.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_reply_actions_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_reply_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_with_replies.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/comment_with_replies_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/episode_card.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/episode_preview_card.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/header.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/post.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/post_actions.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/post_actions_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/post_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/post_with_replies.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/post_with_replies_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/preview_card.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/reblog.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/reblog_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/reply.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/reply_actions.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/reply_actions_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/reply_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/_partials/sidebar.php (100%)
 rename {app/Views => themes/cp_app}/podcast/activity.php (100%)
 rename {app/Views => themes/cp_app}/podcast/activity_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/comment.php (100%)
 rename {app/Views => themes/cp_app}/podcast/comment_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/episode.php (100%)
 rename {app/Views => themes/cp_app}/podcast/episode_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/episodes.php (99%)
 rename {app/Views => themes/cp_app}/podcast/episodes_authenticated.php (99%)
 rename {app/Views => themes/cp_app}/podcast/follow.php (100%)
 rename {app/Views => themes/cp_app}/podcast/post.php (100%)
 rename {app/Views => themes/cp_app}/podcast/post_authenticated.php (100%)
 rename {app/Views => themes/cp_app}/podcast/post_remote_action.php (100%)
 rename {modules/Auth/Views => themes/cp_auth}/_layout.php (97%)
 rename {modules/Auth/Views => themes/cp_auth}/emails/activation.php (100%)
 rename {modules/Auth/Views => themes/cp_auth}/emails/forgot.php (100%)
 rename {modules/Auth/Views => themes/cp_auth}/forgot.php (100%)
 rename {modules/Auth/Views => themes/cp_auth}/login.php (100%)
 create mode 100644 themes/cp_auth/manifest.json
 rename {modules/Auth/Views => themes/cp_auth}/register.php (100%)
 rename {modules/Auth/Views => themes/cp_auth}/reset.php (100%)
 rename {modules/Install/Views => themes/cp_install}/_layout.php (96%)
 rename {modules/Install/Views => themes/cp_install}/cache_config.php (95%)
 rename {modules/Install/Views => themes/cp_install}/create_superadmin.php (96%)
 rename {modules/Install/Views => themes/cp_install}/database_config.php (97%)
 rename {modules/Install/Views => themes/cp_install}/instance_config.php (97%)
 create mode 100644 themes/cp_install/manifest.json
 rename {modules/Install/Views => themes/cp_install}/manual_config.php (89%)

diff --git a/app/Common.php b/app/Common.php
index 7448f1ad83..edbf59bdc2 100644
--- a/app/Common.php
+++ b/app/Common.php
@@ -2,6 +2,9 @@
 
 declare(strict_types=1);
 
+use App\Libraries\View;
+use ViewThemes\Theme;
+
 /**
  * The goal of this file is to allow developers a location where they can overwrite core procedural functions and
  * replace them with their own. This file is loaded during the bootstrap process and is called during the frameworks
@@ -12,3 +15,32 @@ declare(strict_types=1);
  *
  * @link: https://codeigniter4.github.io/CodeIgniter4/
  */
+
+if (! function_exists('view')) {
+    /**
+     * Grabs the current RendererInterface-compatible class and tells it to render the specified view. Simply provides a
+     * convenience method that can be used in Controllers, libraries, and routed closures.
+     *
+     * NOTE: Does not provide any escaping of the data, so that must all be handled manually by the developer.
+     *
+     * @param array<string, mixed>  $data
+     * @param array<string, mixed>  $options Unused - reserved for third-party extensions.
+     */
+    function view(string $name, array $data = [], array $options = []): string
+    {
+        $path = Theme::path();
+
+        /** @var CodeIgniter\View\View $renderer */
+        $renderer = single_service('renderer', $path);
+
+        $saveData = config(View::class)->saveData;
+
+        if (array_key_exists('saveData', $options)) {
+            $saveData = (bool) $options['saveData'];
+            unset($options['saveData']);
+        }
+
+        return $renderer->setData($data, 'raw')
+            ->render($name, $options, $saveData);
+    }
+}
diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php
index 8a6ff73af5..6b313009d7 100644
--- a/app/Config/Autoload.php
+++ b/app/Config/Autoload.php
@@ -51,6 +51,8 @@ class Autoload extends AutoloadConfig
         'Modules\Fediverse' => ROOTPATH . 'modules/Fediverse/',
         'Config' => APPPATH . 'Config/',
         'ViewComponents' => APPPATH . 'Libraries/ViewComponents/',
+        'ViewThemes' => APPPATH . 'Libraries/ViewThemes/',
+        'Themes' => ROOTPATH . 'themes',
     ];
 
     /**
diff --git a/app/Config/ViewComponents.php b/app/Config/ViewComponents.php
index 25afea847f..ae0a31db6b 100644
--- a/app/Config/ViewComponents.php
+++ b/app/Config/ViewComponents.php
@@ -9,14 +9,12 @@ use ViewComponents\Config\ViewComponents as ViewComponentsConfig;
 class ViewComponents extends ViewComponentsConfig
 {
     /**
-     * @var array<string, string>
+     * @var string[]
      */
-    public array $lookupModules = [
-        APP_NAMESPACE => APPPATH,
-        'Modules\Admin' => ROOTPATH . 'modules/Admin/',
-        'Modules\Auth' => ROOTPATH . 'modules/Auth/',
-        'Modules\Analytics' => ROOTPATH . 'modules/Analytics/',
-        'Modules\Install' => ROOTPATH . 'modules/Install/',
-        'Modules\Fediverse' => ROOTPATH . 'modules/Fediverse/',
+    public array $lookupPaths = [
+        ROOTPATH . 'themes/cp_app/',
+        ROOTPATH . 'themes/cp_admin/',
+        ROOTPATH . 'themes/cp_auth/',
+        ROOTPATH . 'themes/cp_install/',
     ];
 }
diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php
index b76cf7b206..2d30eb3c56 100644
--- a/app/Controllers/BaseController.php
+++ b/app/Controllers/BaseController.php
@@ -8,6 +8,7 @@ use CodeIgniter\Controller;
 use CodeIgniter\HTTP\RequestInterface;
 use CodeIgniter\HTTP\ResponseInterface;
 use Psr\Log\LoggerInterface;
+use ViewThemes\Theme;
 
 /**
  * Class BaseController
@@ -19,14 +20,6 @@ use Psr\Log\LoggerInterface;
  */
 class BaseController extends Controller
 {
-    /**
-     * An array of helpers to be loaded automatically upon class instantiation. These helpers will be available to all
-     * other controllers that extend BaseController.
-     *
-     * @var string[]
-     */
-    protected $helpers = ['auth', 'svg', 'components', 'misc'];
-
     /**
      * Constructor.
      */
@@ -35,7 +28,11 @@ class BaseController extends Controller
         ResponseInterface $response,
         LoggerInterface $logger
     ): void {
+        $this->helpers = array_merge($this->helpers, ['auth', 'svg', 'components', 'misc']);
+
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
+
+        Theme::setTheme('app');
     }
 }
diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php
index df1b2bb5b4..f19eb74d1e 100644
--- a/app/Controllers/HomeController.php
+++ b/app/Controllers/HomeController.php
@@ -38,6 +38,7 @@ class HomeController extends BaseController
         $data = [
             'podcasts' => $allPodcasts,
         ];
+
         return view('home', $data);
     }
 }
diff --git a/app/Libraries/View.php b/app/Libraries/View.php
index 38fd82f9b1..4215dd1a41 100644
--- a/app/Libraries/View.php
+++ b/app/Libraries/View.php
@@ -110,7 +110,7 @@ class View extends CodeIgniterView
         }
 
         $output = service('components')
-            ->setCurrentView($view)
+            ->setCurrentView($this->renderVars['file'])
             ->render($output);
 
         $this->logPerformance($this->renderVars['start'], microtime(true), $this->renderVars['view']);
diff --git a/app/Libraries/ViewComponents/ComponentRenderer.php b/app/Libraries/ViewComponents/ComponentRenderer.php
index 38a8e9ecc4..ccac323990 100644
--- a/app/Libraries/ViewComponents/ComponentRenderer.php
+++ b/app/Libraries/ViewComponents/ComponentRenderer.php
@@ -137,26 +137,26 @@ class ComponentRenderer
     private function locateView(string $name): string
     {
         // TODO: Is there a better way to locate components local to current module?
-        $modulesToDiscover = [];
-        $lookupModules = $this->config->lookupModules;
-        $modulesToDiscover = array_filter($lookupModules, function ($namespace): bool {
-            return str_starts_with($this->currentView, $namespace);
-        }, ARRAY_FILTER_USE_KEY);
-        $modulesToDiscover = array_values($modulesToDiscover);
-        $modulesToDiscover[] = $this->config->defaultLookupPath;
+        $pathsToDiscover = [];
+        $lookupPaths = $this->config->lookupPaths;
+        $pathsToDiscover = array_filter($lookupPaths, function ($path): bool {
+            return str_starts_with($this->currentView, $path);
+        });
+        $pathsToDiscover = array_values($pathsToDiscover);
+        $pathsToDiscover[] = $this->config->defaultLookupPath;
 
         $namePath = str_replace('.', '/', $name);
 
-        foreach ($modulesToDiscover as $basePath) {
+        foreach ($pathsToDiscover as $basePath) {
             // Look for a class component first
-            $filePath = $basePath . $this->config->classComponentsPath . '/' . $namePath . '.php';
+            $filePath = $basePath . $this->config->componentsDirectory . '/' . $namePath . '.php';
 
             if (is_file($filePath)) {
                 return $filePath;
             }
 
-            $camelCaseName = strtolower(preg_replace('~(?<!^)(?<!\/)[A-Z]~', '_$0', $namePath) ?? '');
-            $filePath = $basePath . $this->config->viewFileComponentsPath . '/' . $camelCaseName . '.php';
+            $snakeCaseName = strtolower(preg_replace('~(?<!^)(?<!\/)[A-Z]~', '_$0', $namePath) ?? '');
+            $filePath = $basePath . $this->config->componentsDirectory . '/' . $snakeCaseName . '.php';
 
             if (is_file($filePath)) {
                 return $filePath;
diff --git a/app/Libraries/ViewComponents/Config/ViewComponents.php b/app/Libraries/ViewComponents/Config/ViewComponents.php
index 56c041fb1e..4ad7e82ed9 100644
--- a/app/Libraries/ViewComponents/Config/ViewComponents.php
+++ b/app/Libraries/ViewComponents/Config/ViewComponents.php
@@ -8,17 +8,14 @@ use CodeIgniter\Config\BaseConfig;
 
 class ViewComponents extends BaseConfig
 {
-    public string $classComponentsPath = 'View/Components';
-
-    public string $viewFileComponentsPath = 'Views/components';
+    public string $componentsDirectory = 'Components';
 
     /**
-     * Modules to look into for local components. Associative array with the module namespace as key and the module path
-     * as value.
+     * Paths to look into for local components. Will look for the $componentsDirectory inside.
      *
-     * @var array<string, string>
+     * @var string[]
      */
-    public array $lookupModules = [];
+    public array $lookupPaths = [];
 
-    public string $defaultLookupPath = APPPATH;
+    public string $defaultLookupPath = APPPATH . 'Views/';
 }
diff --git a/app/Libraries/ViewThemes/Config/Themes.php b/app/Libraries/ViewThemes/Config/Themes.php
new file mode 100644
index 0000000000..c22ac27cad
--- /dev/null
+++ b/app/Libraries/ViewThemes/Config/Themes.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ViewThemes\Config;
+
+use CodeIgniter\Config\BaseConfig;
+
+class Themes extends BaseConfig
+{
+    public string $themesDirectory = ROOTPATH . 'themes';
+
+    public string $manifestFilename = 'manifest.json';
+
+    /**
+     * @var array<string, string>
+     */
+    public array $themes = [
+        'app' => 'cp_app',
+        'admin' => 'cp_admin',
+        'install' => 'cp_install',
+        'auth' => 'cp_auth',
+    ];
+}
diff --git a/app/Libraries/ViewThemes/Theme.php b/app/Libraries/ViewThemes/Theme.php
new file mode 100644
index 0000000000..30020dcb77
--- /dev/null
+++ b/app/Libraries/ViewThemes/Theme.php
@@ -0,0 +1,99 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ViewThemes;
+
+use ViewThemes\Config\Themes;
+
+/**
+ * Borrowed and adapted from https://github.com/lonnieezell/Bonfire2
+ */
+class Theme
+{
+    protected Themes $config;
+
+    /**
+     * @var string
+     */
+    protected static $defaultTheme = 'app';
+
+    /**
+     * @var string
+     */
+    protected static $currentTheme;
+
+    /**
+     * Holds theme info retrieved
+     *
+     * @var array<int, array<string, mixed>>
+     */
+    protected static array $info = [];
+
+    public function __construct()
+    {
+        $this->config = config('Themes');
+    }
+
+    /**
+     * Sets the active theme.
+     */
+    public static function setTheme(string $theme): void
+    {
+        static::$currentTheme = $theme;
+    }
+
+    /**
+     * Returns the path to the specified theme folder. If no theme is provided, will use the current theme.
+     */
+    public static function path(string $theme = null): string
+    {
+        if ($theme === null) {
+            $theme = static::current();
+        }
+
+        // Ensure we've pulled the theme info
+        if (static::$info === []) {
+            static::$info = self::available();
+        }
+
+        foreach (static::$info as $info) {
+            if ($info['name'] === $theme) {
+                return $info['path'];
+            }
+        }
+
+        return '';
+    }
+
+    /**
+     * Returns the name of the active theme.
+     */
+    public static function current(): string
+    {
+        return static::$currentTheme !== null
+            ? static::$currentTheme
+            : static::$defaultTheme;
+    }
+
+    /**
+     * Returns an array of all available themes and the paths to their directories.
+     *
+     * @return array<int, array<string, mixed>>
+     */
+    public static function available(): array
+    {
+        $themes = [];
+
+        $config = config('Themes');
+
+        foreach ($config->themes as $name => $folder) {
+            $themes[] = [
+                'name' => $name,
+                'path' => $config->themesDirectory . '/' . $folder . '/',
+            ];
+        }
+
+        return $themes;
+    }
+}
diff --git a/app/View/Components/Button.php b/app/Views/Components/Button.php
similarity index 98%
rename from app/View/Components/Button.php
rename to app/Views/Components/Button.php
index b122395f90..3ba713fde4 100644
--- a/app/View/Components/Button.php
+++ b/app/Views/Components/Button.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components;
+namespace App\Views\Components;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Forms/Label.php b/app/Views/Components/Forms/Label.php
similarity index 96%
rename from app/View/Components/Forms/Label.php
rename to app/Views/Components/Forms/Label.php
index b2dfd00167..6ff709d463 100644
--- a/app/View/Components/Forms/Label.php
+++ b/app/Views/Components/Forms/Label.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components\Forms;
+namespace App\Views\Components\Forms;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Forms/MarkdownEditor.php b/app/Views/Components/Forms/MarkdownEditor.php
similarity index 99%
rename from app/View/Components/Forms/MarkdownEditor.php
rename to app/Views/Components/Forms/MarkdownEditor.php
index 6540e282ac..febf2ebe04 100644
--- a/app/View/Components/Forms/MarkdownEditor.php
+++ b/app/Views/Components/Forms/MarkdownEditor.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components\Forms;
+namespace App\Views\Components\Forms;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Forms/MultiSelect.php b/app/Views/Components/Forms/MultiSelect.php
similarity index 95%
rename from app/View/Components/Forms/MultiSelect.php
rename to app/Views/Components/Forms/MultiSelect.php
index af7a24c3ce..f9c25c0723 100644
--- a/app/View/Components/Forms/MultiSelect.php
+++ b/app/Views/Components/Forms/MultiSelect.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components\Forms;
+namespace App\Views\Components\Forms;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Forms/Select.php b/app/Views/Components/Forms/Select.php
similarity index 94%
rename from app/View/Components/Forms/Select.php
rename to app/Views/Components/Forms/Select.php
index ea9374b63a..050c3cf334 100644
--- a/app/View/Components/Forms/Select.php
+++ b/app/Views/Components/Forms/Select.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components\Forms;
+namespace App\Views\Components\Forms;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Forms/Toggler.php b/app/Views/Components/Forms/Toggler.php
similarity index 97%
rename from app/View/Components/Forms/Toggler.php
rename to app/Views/Components/Forms/Toggler.php
index eab1268a4e..a8ae1e8769 100644
--- a/app/View/Components/Forms/Toggler.php
+++ b/app/Views/Components/Forms/Toggler.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components\Forms;
+namespace App\Views\Components\Forms;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Forms/XMLEditor.php b/app/Views/Components/Forms/XMLEditor.php
similarity index 93%
rename from app/View/Components/Forms/XMLEditor.php
rename to app/Views/Components/Forms/XMLEditor.php
index ac03f3d0b6..71e01d242e 100644
--- a/app/View/Components/Forms/XMLEditor.php
+++ b/app/Views/Components/Forms/XMLEditor.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components\Forms;
+namespace App\Views\Components\Forms;
 
 use ViewComponents\Component;
 
diff --git a/app/View/Components/Icon.php b/app/Views/Components/Icon.php
similarity index 94%
rename from app/View/Components/Icon.php
rename to app/Views/Components/Icon.php
index 7cd44991dc..a1b7577063 100644
--- a/app/View/Components/Icon.php
+++ b/app/Views/Components/Icon.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace App\View\Components;
+namespace App\Views\Components;
 
 use Exception;
 use ViewComponents\Component;
diff --git a/app/Views/components/.gitkeep b/app/Views/components/.gitkeep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/modules/Admin/Controllers/BaseController.php b/modules/Admin/Controllers/BaseController.php
index 205a4f4aa2..a38b8e8457 100644
--- a/modules/Admin/Controllers/BaseController.php
+++ b/modules/Admin/Controllers/BaseController.php
@@ -8,6 +8,7 @@ use CodeIgniter\Controller;
 use CodeIgniter\HTTP\RequestInterface;
 use CodeIgniter\HTTP\ResponseInterface;
 use Psr\Log\LoggerInterface;
+use ViewThemes\Theme;
 
 /**
  * Class BaseController
@@ -20,14 +21,6 @@ use Psr\Log\LoggerInterface;
 
 class BaseController extends Controller
 {
-    /**
-     * An array of helpers to be loaded automatically upon class instantiation. These helpers will be available to all
-     * other controllers that extend BaseController.
-     *
-     * @var string[]
-     */
-    protected $helpers = ['auth', 'breadcrumb', 'svg', 'components', 'misc'];
-
     /**
      * Constructor.
      */
@@ -36,7 +29,11 @@ class BaseController extends Controller
         ResponseInterface $response,
         LoggerInterface $logger
     ): void {
+        $this->helpers = array_merge($this->helpers, ['auth', 'breadcrumb', 'svg', 'components', 'misc']);
+
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
+
+        Theme::setTheme('admin');
     }
 }
diff --git a/modules/Admin/Controllers/ContributorController.php b/modules/Admin/Controllers/ContributorController.php
index 47a825c304..ad79f24b50 100644
--- a/modules/Admin/Controllers/ContributorController.php
+++ b/modules/Admin/Controllers/ContributorController.php
@@ -57,7 +57,7 @@ class ContributorController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\contributor\list', $data);
+        return view('contributor/list', $data);
     }
 
     public function view(): string
@@ -70,7 +70,7 @@ class ContributorController extends BaseController
             0 => $this->podcast->title,
             1 => $this->user->username,
         ]);
-        return view('Modules\Admin\Views\contributor\view', $data);
+        return view('contributor/view', $data);
     }
 
     public function add(): string
@@ -106,7 +106,7 @@ class ContributorController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\contributor\add', $data);
+        return view('contributor/add', $data);
     }
 
     public function attemptAdd(): RedirectResponse
@@ -155,7 +155,7 @@ class ContributorController extends BaseController
             0 => $this->podcast->title,
             1 => $this->user->username,
         ]);
-        return view('Modules\Admin\Views\contributor\edit', $data);
+        return view('contributor/edit', $data);
     }
 
     public function attemptEdit(): RedirectResponse
diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php
index 5791a7c330..8716e64199 100644
--- a/modules/Admin/Controllers/EpisodeController.php
+++ b/modules/Admin/Controllers/EpisodeController.php
@@ -77,7 +77,7 @@ class EpisodeController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\episode\list', $data);
+        return view('episode/list', $data);
     }
 
     public function view(): string
@@ -91,7 +91,7 @@ class EpisodeController extends BaseController
             0 => $this->podcast->title,
             1 => $this->episode->title,
         ]);
-        return view('Modules\Admin\Views\episode\view', $data);
+        return view('episode/view', $data);
     }
 
     public function create(): string
@@ -105,7 +105,7 @@ class EpisodeController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\episode\create', $data);
+        return view('episode/create', $data);
     }
 
     public function attemptCreate(): RedirectResponse
@@ -230,7 +230,7 @@ class EpisodeController extends BaseController
             0 => $this->podcast->title,
             1 => $this->episode->title,
         ]);
-        return view('Modules\Admin\Views\episode\edit', $data);
+        return view('episode/edit', $data);
     }
 
     public function attemptEdit(): RedirectResponse
@@ -404,7 +404,7 @@ class EpisodeController extends BaseController
                 0 => $this->podcast->title,
                 1 => $this->episode->title,
             ]);
-            return view('Modules\Admin\Views\episode\publish', $data);
+            return view('episode/publish', $data);
         }
 
         return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
@@ -503,7 +503,7 @@ class EpisodeController extends BaseController
                 0 => $this->podcast->title,
                 1 => $this->episode->title,
             ]);
-            return view('Modules\Admin\Views\episode\publish_edit', $data);
+            return view('episode/publish_edit', $data);
         }
 
         return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
@@ -632,7 +632,7 @@ class EpisodeController extends BaseController
                 0 => $this->podcast->title,
                 1 => $this->episode->title,
             ]);
-            return view('Modules\Admin\Views\episode\unpublish', $data);
+            return view('episode/unpublish', $data);
         }
 
         return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
@@ -704,7 +704,7 @@ class EpisodeController extends BaseController
             0 => $this->podcast->title,
             1 => $this->episode->title,
         ]);
-        return view('Modules\Admin\Views\episode\soundbites', $data);
+        return view('episode/soundbites', $data);
     }
 
     public function soundbitesAttemptEdit(): RedirectResponse
@@ -782,7 +782,7 @@ class EpisodeController extends BaseController
             0 => $this->podcast->title,
             1 => $this->episode->title,
         ]);
-        return view('Modules\Admin\Views\episode\embeddable_player', $data);
+        return view('episode/embeddable_player', $data);
     }
 
     public function attemptCommentCreate(): RedirectResponse
diff --git a/modules/Admin/Controllers/EpisodePersonController.php b/modules/Admin/Controllers/EpisodePersonController.php
index 860b9ca945..1749157ef3 100644
--- a/modules/Admin/Controllers/EpisodePersonController.php
+++ b/modules/Admin/Controllers/EpisodePersonController.php
@@ -62,7 +62,7 @@ class EpisodePersonController extends BaseController
             0 => $this->podcast->title,
             1 => $this->episode->title,
         ]);
-        return view('Modules\Admin\Views\episode\persons', $data);
+        return view('episode/persons', $data);
     }
 
     public function attemptAdd(): RedirectResponse
diff --git a/modules/Admin/Controllers/FediverseController.php b/modules/Admin/Controllers/FediverseController.php
index c1dbd41a74..134939edf0 100644
--- a/modules/Admin/Controllers/FediverseController.php
+++ b/modules/Admin/Controllers/FediverseController.php
@@ -14,7 +14,7 @@ class FediverseController extends BaseController
 {
     public function dashboard(): string
     {
-        return view('Modules\Admin\Views\fediverse\dashboard');
+        return view('fediverse/dashboard');
     }
 
     public function blockedActors(): string
@@ -24,7 +24,7 @@ class FediverseController extends BaseController
         $blockedActors = model('ActorModel')
             ->getBlockedActors();
 
-        return view('Modules\Admin\Views\fediverse\blocked_actors', [
+        return view('fediverse/blocked_actors', [
             'blockedActors' => $blockedActors,
         ]);
     }
@@ -36,7 +36,7 @@ class FediverseController extends BaseController
         $blockedDomains = model('BlockedDomainModel')
             ->getBlockedDomains();
 
-        return view('Modules\Admin\Views\fediverse\blocked_domains', [
+        return view('fediverse/blocked_domains', [
             'blockedDomains' => $blockedDomains,
         ]);
     }
diff --git a/modules/Admin/Controllers/MyAccountController.php b/modules/Admin/Controllers/MyAccountController.php
index 89fa6d4111..9314724970 100644
--- a/modules/Admin/Controllers/MyAccountController.php
+++ b/modules/Admin/Controllers/MyAccountController.php
@@ -18,14 +18,14 @@ class MyAccountController extends BaseController
 {
     public function index(): string
     {
-        return view('Modules\Admin\Views\my_account\view');
+        return view('my_account\view');
     }
 
     public function changePassword(): string
     {
         helper('form');
 
-        return view('Modules\Admin\Views\my_account\change_password');
+        return view('my_account\change_password');
     }
 
     public function attemptChange(): RedirectResponse
diff --git a/modules/Admin/Controllers/PageController.php b/modules/Admin/Controllers/PageController.php
index 4071f4080d..7458719ee1 100644
--- a/modules/Admin/Controllers/PageController.php
+++ b/modules/Admin/Controllers/PageController.php
@@ -38,12 +38,12 @@ class PageController extends BaseController
             'pages' => (new PageModel())->findAll(),
         ];
 
-        return view('Modules\Admin\Views\page\list', $data);
+        return view('page/list', $data);
     }
 
     public function view(): string
     {
-        return view('Modules\Admin\Views\page\view', [
+        return view('page/view', [
             'page' => $this->page,
         ]);
     }
@@ -52,7 +52,7 @@ class PageController extends BaseController
     {
         helper('form');
 
-        return view('Modules\Admin\Views\page\create');
+        return view('page/create');
     }
 
     public function attemptCreate(): RedirectResponse
@@ -86,7 +86,7 @@ class PageController extends BaseController
         replace_breadcrumb_params([
             0 => $this->page->title,
         ]);
-        return view('Modules\Admin\Views\page\edit', [
+        return view('page/edit', [
             'page' => $this->page,
         ]);
     }
diff --git a/modules/Admin/Controllers/PersonController.php b/modules/Admin/Controllers/PersonController.php
index 1aa8cb46b4..3d75828c52 100644
--- a/modules/Admin/Controllers/PersonController.php
+++ b/modules/Admin/Controllers/PersonController.php
@@ -41,7 +41,7 @@ class PersonController extends BaseController
             'persons' => (new PersonModel())->findAll(),
         ];
 
-        return view('Modules\Admin\Views\person\list', $data);
+        return view('person/list', $data);
     }
 
     public function view(): string
@@ -53,14 +53,14 @@ class PersonController extends BaseController
         replace_breadcrumb_params([
             0 => $this->person->full_name,
         ]);
-        return view('Modules\Admin\Views\person\view', $data);
+        return view('person/view', $data);
     }
 
     public function create(): string
     {
         helper(['form']);
 
-        return view('Modules\Admin\Views\person\create');
+        return view('person/create');
     }
 
     public function attemptCreate(): RedirectResponse
@@ -113,7 +113,7 @@ class PersonController extends BaseController
         replace_breadcrumb_params([
             0 => $this->person->full_name,
         ]);
-        return view('Modules\Admin\Views\person\edit', $data);
+        return view('person/edit', $data);
     }
 
     public function attemptEdit(): RedirectResponse
diff --git a/modules/Admin/Controllers/PodcastController.php b/modules/Admin/Controllers/PodcastController.php
index 34c76228bc..6999c7efbe 100644
--- a/modules/Admin/Controllers/PodcastController.php
+++ b/modules/Admin/Controllers/PodcastController.php
@@ -53,7 +53,7 @@ class PodcastController extends BaseController
             ];
         }
 
-        return view('Modules\Admin\Views\podcast\list', $data);
+        return view('podcast/list', $data);
     }
 
     public function view(): string
@@ -65,7 +65,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\view', $data);
+        return view('podcast/view', $data);
     }
 
     public function viewAnalytics(): string
@@ -77,7 +77,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\index', $data);
+        return view('podcast/analytics/index', $data);
     }
 
     public function viewAnalyticsWebpages(): string
@@ -89,7 +89,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\webpages', $data);
+        return view('podcast/analytics/webpages', $data);
     }
 
     public function viewAnalyticsLocations(): string
@@ -101,7 +101,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\locations', $data);
+        return view('podcast/analytics/locations', $data);
     }
 
     public function viewAnalyticsUniqueListeners(): string
@@ -113,7 +113,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\unique_listeners', $data);
+        return view('podcast/analytics/unique_listeners', $data);
     }
 
     public function viewAnalyticsListeningTime(): string
@@ -125,7 +125,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\listening_time', $data);
+        return view('podcast/analytics/listening_time', $data);
     }
 
     public function viewAnalyticsTimePeriods(): string
@@ -137,7 +137,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\time_periods', $data);
+        return view('podcast/analytics/time_periods', $data);
     }
 
     public function viewAnalyticsPlayers(): string
@@ -149,7 +149,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\analytics\players', $data);
+        return view('podcast/analytics/players', $data);
     }
 
     public function create(): string
@@ -165,7 +165,7 @@ class PodcastController extends BaseController
             'browserLang' => get_browser_language($this->request->getServer('HTTP_ACCEPT_LANGUAGE')),
         ];
 
-        return view('Modules\Admin\Views\podcast\create', $data);
+        return view('podcast/create', $data);
     }
 
     public function attemptCreate(): RedirectResponse
@@ -274,7 +274,7 @@ class PodcastController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\edit', $data);
+        return view('podcast/edit', $data);
     }
 
     public function attemptEdit(): RedirectResponse
@@ -357,15 +357,16 @@ class PodcastController extends BaseController
         return redirect()->route('podcast-view', [$this->podcast->id]);
     }
 
-    public function latestEpisodes(int $limit, int $podcast_id): string
+    public function latestEpisodes(int $limit, int $podcastId): string
     {
         $episodes = (new EpisodeModel())
-            ->where('podcast_id', $podcast_id)
+            ->where('podcast_id', $podcastId)
             ->orderBy('created_at', 'desc')
             ->findAll($limit);
 
-        return view('Modules\Admin\Views\podcast\latest_episodes', [
+        return view('podcast/latest_episodes', [
             'episodes' => $episodes,
+            'podcast' => (new PodcastModel())->getPodcastById($podcastId),
         ]);
     }
 
diff --git a/modules/Admin/Controllers/PodcastImportController.php b/modules/Admin/Controllers/PodcastImportController.php
index 0d84686134..4d750c4477 100644
--- a/modules/Admin/Controllers/PodcastImportController.php
+++ b/modules/Admin/Controllers/PodcastImportController.php
@@ -58,7 +58,7 @@ class PodcastImportController extends BaseController
             'browserLang' => get_browser_language($this->request->getServer('HTTP_ACCEPT_LANGUAGE')),
         ];
 
-        return view('Modules\Admin\Views\podcast\import', $data);
+        return view('podcast/import', $data);
     }
 
     public function attemptImport(): RedirectResponse
diff --git a/modules/Admin/Controllers/PodcastPersonController.php b/modules/Admin/Controllers/PodcastPersonController.php
index 2704018eb5..371c624625 100644
--- a/modules/Admin/Controllers/PodcastPersonController.php
+++ b/modules/Admin/Controllers/PodcastPersonController.php
@@ -49,7 +49,7 @@ class PodcastPersonController extends BaseController
         replace_breadcrumb_params([
             0 => $this->podcast->title,
         ]);
-        return view('Modules\Admin\Views\podcast\persons', $data);
+        return view('podcast/persons', $data);
     }
 
     public function attemptAdd(): RedirectResponse
diff --git a/modules/Admin/Controllers/PodcastPlatformController.php b/modules/Admin/Controllers/PodcastPlatformController.php
index 379a11abfe..bc49bc374e 100644
--- a/modules/Admin/Controllers/PodcastPlatformController.php
+++ b/modules/Admin/Controllers/PodcastPlatformController.php
@@ -39,7 +39,7 @@ class PodcastPlatformController extends BaseController
 
     public function index(): string
     {
-        return view('Modules\Admin\Views\podcast\platforms\dashboard');
+        return view('podcast/platforms\dashboard');
     }
 
     public function platforms(string $platformType): string
@@ -56,9 +56,7 @@ class PodcastPlatformController extends BaseController
             0 => $this->podcast->title,
         ]);
 
-        $view = view('Modules\Admin\Views\podcast\platforms', $data);
-
-        return $view;
+        return view('podcast/platforms', $data);
     }
 
     public function attemptPlatformsUpdate(string $platformType): RedirectResponse
diff --git a/modules/Admin/Controllers/UserController.php b/modules/Admin/Controllers/UserController.php
index 5ce0d32754..15f2c661d5 100644
--- a/modules/Admin/Controllers/UserController.php
+++ b/modules/Admin/Controllers/UserController.php
@@ -40,7 +40,7 @@ class UserController extends BaseController
             'users' => (new UserModel())->findAll(),
         ];
 
-        return view('Modules\Admin\Views\user\list', $data);
+        return view('user/list', $data);
     }
 
     public function view(): string
@@ -52,7 +52,7 @@ class UserController extends BaseController
         replace_breadcrumb_params([
             0 => $this->user->username,
         ]);
-        return view('Modules\Admin\Views\user\view', $data);
+        return view('user/view', $data);
     }
 
     public function create(): string
@@ -63,7 +63,7 @@ class UserController extends BaseController
             'roles' => (new GroupModel())->getUserRoles(),
         ];
 
-        return view('Modules\Admin\Views\user\create', $data);
+        return view('user/create', $data);
     }
 
     public function attemptCreate(): RedirectResponse
@@ -135,7 +135,7 @@ class UserController extends BaseController
         replace_breadcrumb_params([
             0 => $this->user->username,
         ]);
-        return view('Modules\Admin\Views\user\edit', $data);
+        return view('user/edit', $data);
     }
 
     public function attemptEdit(): RedirectResponse
diff --git a/modules/Auth/Config/Auth.php b/modules/Auth/Config/Auth.php
index 98be59e61c..1794d8f861 100644
--- a/modules/Auth/Config/Auth.php
+++ b/modules/Auth/Config/Auth.php
@@ -16,12 +16,12 @@ class Auth extends MythAuthConfig
      * @var array<string, string>
      */
     public $views = [
-        'login' => 'Modules\Auth\Views\login',
-        'register' => 'Modules\Auth\Views\register',
-        'forgot' => 'Modules\Auth\Views\forgot',
-        'reset' => 'Modules\Auth\Views\reset',
-        'emailForgot' => 'Modules\Auth\Views\emails\forgot',
-        'emailActivation' => 'Modules\Auth\Views\emails\activation',
+        'login' => 'login',
+        'register' => 'register',
+        'forgot' => 'forgot',
+        'reset' => 'reset',
+        'emailForgot' => 'emails/forgot',
+        'emailActivation' => 'emails/activation',
     ];
 
     /**
@@ -31,7 +31,7 @@ class Auth extends MythAuthConfig
      *
      * @var string
      */
-    public $viewLayout = 'Modules\Auth\Views\_layout';
+    public $viewLayout = '_layout';
 
     /**
      * --------------------------------------------------------------------------
diff --git a/modules/Auth/Controllers/AuthController.php b/modules/Auth/Controllers/AuthController.php
index 6163e0b444..10c23d7a25 100644
--- a/modules/Auth/Controllers/AuthController.php
+++ b/modules/Auth/Controllers/AuthController.php
@@ -13,6 +13,7 @@ namespace Modules\Auth\Controllers;
 use CodeIgniter\HTTP\RedirectResponse;
 use Modules\Auth\Entities\User;
 use Myth\Auth\Controllers\AuthController as MythAuthController;
+use ViewThemes\Theme;
 
 class AuthController extends MythAuthController
 {
@@ -23,6 +24,13 @@ class AuthController extends MythAuthController
      */
     protected $helpers = ['components'];
 
+    public function __construct()
+    {
+        parent::__construct();
+
+        Theme::setTheme('auth');
+    }
+
     /**
      * Attempt to register a new user.
      */
diff --git a/modules/Install/Controllers/InstallController.php b/modules/Install/Controllers/InstallController.php
index 1b0e8cada1..63260eebb9 100644
--- a/modules/Install/Controllers/InstallController.php
+++ b/modules/Install/Controllers/InstallController.php
@@ -24,6 +24,7 @@ use Dotenv\Exception\ValidationException;
 use Modules\Auth\Entities\User;
 use Psr\Log\LoggerInterface;
 use Throwable;
+use ViewThemes\Theme;
 
 class InstallController extends Controller
 {
@@ -42,6 +43,8 @@ class InstallController extends Controller
     ): void {
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
+
+        Theme::setTheme('install');
     }
 
     /**
@@ -58,7 +61,7 @@ class InstallController extends Controller
                 fclose($envFile);
             } catch (Throwable) {
                 // Could not create the .env file, redirect to a view with instructions on how to add it manually
-                return view('Modules\Install\Views\manual_config');
+                return view('manual_config');
             }
         }
 
@@ -106,7 +109,7 @@ class InstallController extends Controller
                     'cache.handler',
                 ]);
             } catch (ValidationException) {
-                return view('Modules\Install\Views\manual_config');
+                return view('manual_config');
             }
         }
 
@@ -127,7 +130,7 @@ class InstallController extends Controller
             session()
                 ->setFlashdata('error', lang('Install.messages.databaseConnectError'));
 
-            return view('Modules\Install\Views\database_config');
+            return view('database_config');
         }
 
         // migrate if no user has been created
@@ -141,7 +144,7 @@ class InstallController extends Controller
 
     public function instanceConfig(): string
     {
-        return view('Modules\Install\Views\instance_config');
+        return view('instance_config');
     }
 
     public function attemptInstanceConfig(): RedirectResponse
@@ -178,7 +181,7 @@ class InstallController extends Controller
 
     public function databaseConfig(): string
     {
-        return view('Modules\Install\Views\database_config');
+        return view('database_config');
     }
 
     public function attemptDatabaseConfig(): RedirectResponse
@@ -210,7 +213,7 @@ class InstallController extends Controller
 
     public function cacheConfig(): string
     {
-        return view('Modules\Install\Views\cache_config');
+        return view('cache_config');
     }
 
     public function attemptCacheConfig(): RedirectResponse
@@ -268,7 +271,7 @@ class InstallController extends Controller
      */
     public function createSuperAdmin(): string
     {
-        return view('Modules\Install\Views\create_superadmin');
+        return view('create_superadmin');
     }
 
     /**
diff --git a/phpstan.neon b/phpstan.neon
index 43e45fc773..8db6aaed65 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -16,6 +16,7 @@ parameters:
         - app/Libraries/Router.php
         - app/Views/*
         - modules/*/Views/*
+        - themes/*
     ignoreErrors:
         - '#This property type might be inlined to PHP. Do you have confidence it is correct\? Put it here#'
         - '#^Cognitive complexity for#'
@@ -31,3 +32,4 @@ parameters:
             message: '#Function "function_exists\(\)" cannot be used/left in the code#'
             paths:
                 - app/Helpers
+                - app/Common.php
diff --git a/tailwind.config.js b/tailwind.config.js
index 922fe549fc..ce028e812b 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -6,6 +6,7 @@ module.exports = {
     "./app/Views/**/*.php",
     "./app/View/Components/**/*.php",
     "./modules/**/Views/**/*.php",
+    "./themes/**/*.php",
     "./app/Helpers/*.php",
     "./app/Resources/**/*.ts",
   ],
diff --git a/modules/Admin/Views/_layout.php b/themes/cp_admin/_layout.php
similarity index 93%
rename from modules/Admin/Views/_layout.php
rename to themes/cp_admin/_layout.php
index 1232eb903e..9ecdaa3f57 100644
--- a/modules/Admin/Views/_layout.php
+++ b/themes/cp_admin/_layout.php
@@ -17,9 +17,9 @@
     <div id="sidebar-backdrop" role="button" tabIndex="0" aria-label="Close" class="fixed z-50 hidden w-full h-full bg-gray-900 bg-opacity-50 md:hidden"></div>
     <aside id="admin-sidebar" class="sticky top-0 z-50 flex flex-col max-h-screen transition duration-200 ease-in-out transform -translate-x-full bg-white border-r w-80 holy-grail-sidebar md:translate-x-0">
         <?php if (isset($podcast)): ?>
-            <?= $this->include('Modules\Admin\Views\podcast\_sidebar') ?>
+            <?= $this->include('podcast/_sidebar') ?>
         <?php else: ?>
-            <?= $this->include('Modules\Admin\Views\_sidebar') ?>
+            <?= $this->include('_sidebar') ?>
         <?php endif; ?>
     </aside>
     <main class="holy-grail-main">
@@ -40,7 +40,7 @@
             </div>
         </header>
         <div class="container px-2 py-8 mx-auto md:px-12">
-            <?= view('_message_block') ?>
+            <!-- view('App\Views\_message_block') -->
             <?= $this->renderSection('content') ?>
         </div>
     </main>
diff --git a/modules/Admin/Views/_partials/_user_info.php b/themes/cp_admin/_partials/_user_info.php
similarity index 100%
rename from modules/Admin/Views/_partials/_user_info.php
rename to themes/cp_admin/_partials/_user_info.php
diff --git a/modules/Admin/Views/_sidebar.php b/themes/cp_admin/_sidebar.php
similarity index 100%
rename from modules/Admin/Views/_sidebar.php
rename to themes/cp_admin/_sidebar.php
diff --git a/modules/Admin/Views/contributor/add.php b/themes/cp_admin/contributor/add.php
similarity index 95%
rename from modules/Admin/Views/contributor/add.php
rename to themes/cp_admin/contributor/add.php
index 4fbbf7c2f7..9accb73b0e 100644
--- a/modules/Admin/Views/contributor/add.php
+++ b/themes/cp_admin/contributor/add.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Contributor.add_contributor', [$podcast->title]) ?>
diff --git a/modules/Admin/Views/contributor/edit.php b/themes/cp_admin/contributor/edit.php
similarity index 94%
rename from modules/Admin/Views/contributor/edit.php
rename to themes/cp_admin/contributor/edit.php
index b49818e079..dd93510e5f 100644
--- a/modules/Admin/Views/contributor/edit.php
+++ b/themes/cp_admin/contributor/edit.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Contributor.edit_role', [$user->username]) ?>
diff --git a/modules/Admin/Views/contributor/list.php b/themes/cp_admin/contributor/list.php
similarity index 97%
rename from modules/Admin/Views/contributor/list.php
rename to themes/cp_admin/contributor/list.php
index 60d1c18a11..49b440f580 100644
--- a/modules/Admin/Views/contributor/list.php
+++ b/themes/cp_admin/contributor/list.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Contributor.podcast_contributors') ?>
diff --git a/modules/Admin/Views/contributor/view.php b/themes/cp_admin/contributor/view.php
similarity index 94%
rename from modules/Admin/Views/contributor/view.php
rename to themes/cp_admin/contributor/view.php
index 98bcb19601..7476b15e62 100644
--- a/modules/Admin/Views/contributor/view.php
+++ b/themes/cp_admin/contributor/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Contributor.view', [
diff --git a/modules/Admin/Views/dashboard.php b/themes/cp_admin/dashboard.php
similarity index 85%
rename from modules/Admin/Views/dashboard.php
rename to themes/cp_admin/dashboard.php
index 3db6af697f..7dce6c0d1d 100644
--- a/modules/Admin/Views/dashboard.php
+++ b/themes/cp_admin/dashboard.php
@@ -1,5 +1,5 @@
 <?= helper('components') ?>
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Admin.dashboard') ?>
diff --git a/modules/Admin/Views/episode/create.php b/themes/cp_admin/episode/create.php
similarity index 99%
rename from modules/Admin/Views/episode/create.php
rename to themes/cp_admin/episode/create.php
index b6bae04860..86810d0f45 100644
--- a/modules/Admin/Views/episode/create.php
+++ b/themes/cp_admin/episode/create.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.create') ?>
diff --git a/modules/Admin/Views/episode/edit.php b/themes/cp_admin/episode/edit.php
similarity index 99%
rename from modules/Admin/Views/episode/edit.php
rename to themes/cp_admin/episode/edit.php
index 051968d224..be1470944f 100644
--- a/modules/Admin/Views/episode/edit.php
+++ b/themes/cp_admin/episode/edit.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.edit') ?>
diff --git a/modules/Admin/Views/episode/embeddable_player.php b/themes/cp_admin/episode/embeddable_player.php
similarity index 97%
rename from modules/Admin/Views/episode/embeddable_player.php
rename to themes/cp_admin/episode/embeddable_player.php
index 7738c1f342..207b172be4 100644
--- a/modules/Admin/Views/episode/embeddable_player.php
+++ b/themes/cp_admin/episode/embeddable_player.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.embeddable_player.title') ?>
diff --git a/modules/Admin/Views/episode/list.php b/themes/cp_admin/episode/list.php
similarity index 99%
rename from modules/Admin/Views/episode/list.php
rename to themes/cp_admin/episode/list.php
index b67f42ebb5..6e033f3983 100644
--- a/modules/Admin/Views/episode/list.php
+++ b/themes/cp_admin/episode/list.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.all_podcast_episodes') ?>
diff --git a/modules/Admin/Views/episode/persons.php b/themes/cp_admin/episode/persons.php
similarity index 98%
rename from modules/Admin/Views/episode/persons.php
rename to themes/cp_admin/episode/persons.php
index aca39770c0..dca30225d4 100644
--- a/modules/Admin/Views/episode/persons.php
+++ b/themes/cp_admin/episode/persons.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Person.episode_form.title') ?>
diff --git a/modules/Admin/Views/episode/publish.php b/themes/cp_admin/episode/publish.php
similarity index 99%
rename from modules/Admin/Views/episode/publish.php
rename to themes/cp_admin/episode/publish.php
index 9804b2cd42..1e80bc7541 100644
--- a/modules/Admin/Views/episode/publish.php
+++ b/themes/cp_admin/episode/publish.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.publish') ?>
diff --git a/modules/Admin/Views/episode/publish_edit.php b/themes/cp_admin/episode/publish_edit.php
similarity index 99%
rename from modules/Admin/Views/episode/publish_edit.php
rename to themes/cp_admin/episode/publish_edit.php
index 08cdb94dd9..bfa93ed97f 100644
--- a/modules/Admin/Views/episode/publish_edit.php
+++ b/themes/cp_admin/episode/publish_edit.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.publish_edit') ?>
diff --git a/modules/Admin/Views/episode/soundbites.php b/themes/cp_admin/episode/soundbites.php
similarity index 99%
rename from modules/Admin/Views/episode/soundbites.php
rename to themes/cp_admin/episode/soundbites.php
index dd97aecb44..08248097e4 100644
--- a/modules/Admin/Views/episode/soundbites.php
+++ b/themes/cp_admin/episode/soundbites.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.soundbites_form.title') ?>
diff --git a/modules/Admin/Views/episode/unpublish.php b/themes/cp_admin/episode/unpublish.php
similarity index 96%
rename from modules/Admin/Views/episode/unpublish.php
rename to themes/cp_admin/episode/unpublish.php
index 3deeb58331..17fe5db35d 100644
--- a/modules/Admin/Views/episode/unpublish.php
+++ b/themes/cp_admin/episode/unpublish.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Episode.unpublish') ?>
diff --git a/modules/Admin/Views/episode/view.php b/themes/cp_admin/episode/view.php
similarity index 98%
rename from modules/Admin/Views/episode/view.php
rename to themes/cp_admin/episode/view.php
index a4f207c807..54ce4b1519 100644
--- a/modules/Admin/Views/episode/view.php
+++ b/themes/cp_admin/episode/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $episode->title ?>
diff --git a/modules/Admin/Views/fediverse/blocked_actors.php b/themes/cp_admin/fediverse/blocked_actors.php
similarity index 97%
rename from modules/Admin/Views/fediverse/blocked_actors.php
rename to themes/cp_admin/fediverse/blocked_actors.php
index 5d4438e166..af2bab9ea8 100644
--- a/modules/Admin/Views/fediverse/blocked_actors.php
+++ b/themes/cp_admin/fediverse/blocked_actors.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Fediverse.blocked_actors') ?>
diff --git a/modules/Admin/Views/fediverse/blocked_domains.php b/themes/cp_admin/fediverse/blocked_domains.php
similarity index 97%
rename from modules/Admin/Views/fediverse/blocked_domains.php
rename to themes/cp_admin/fediverse/blocked_domains.php
index cdf9df4f5d..77ef604edc 100644
--- a/modules/Admin/Views/fediverse/blocked_domains.php
+++ b/themes/cp_admin/fediverse/blocked_domains.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Fediverse.blocked_domains') ?>
diff --git a/themes/cp_admin/manifest.json b/themes/cp_admin/manifest.json
new file mode 100644
index 0000000000..6509e6a0a2
--- /dev/null
+++ b/themes/cp_admin/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "Castopod Admin",
+  "description": "Castopod's default theme for admin"
+}
diff --git a/modules/Admin/Views/my_account/change_password.php b/themes/cp_admin/my_account/change_password.php
similarity index 95%
rename from modules/Admin/Views/my_account/change_password.php
rename to themes/cp_admin/my_account/change_password.php
index 7cf61b6f56..0cbbe9be80 100644
--- a/modules/Admin/Views/my_account/change_password.php
+++ b/themes/cp_admin/my_account/change_password.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('MyAccount.changePassword') ?>
diff --git a/modules/Admin/Views/my_account/view.php b/themes/cp_admin/my_account/view.php
similarity index 65%
rename from modules/Admin/Views/my_account/view.php
rename to themes/cp_admin/my_account/view.php
index f497adacfd..fc866674ac 100644
--- a/modules/Admin/Views/my_account/view.php
+++ b/themes/cp_admin/my_account/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('MyAccount.info') ?>
@@ -11,6 +11,6 @@
 
 <?= $this->section('content') ?>
 
-<?= view('Modules\Admin\Views\_partials/_user_info.php', ['user' => user()]) ?>
+<?= view('_partials/_user_info.php', ['user' => user()]) ?>
 
 <?= $this->endSection() ?>
diff --git a/modules/Admin/Views/page/create.php b/themes/cp_admin/page/create.php
similarity index 96%
rename from modules/Admin/Views/page/create.php
rename to themes/cp_admin/page/create.php
index 0fccd6b44b..4e22306ca2 100644
--- a/modules/Admin/Views/page/create.php
+++ b/themes/cp_admin/page/create.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Page.create') ?>
diff --git a/modules/Admin/Views/page/edit.php b/themes/cp_admin/page/edit.php
similarity index 96%
rename from modules/Admin/Views/page/edit.php
rename to themes/cp_admin/page/edit.php
index d5ae383ae5..b23f2e756c 100644
--- a/modules/Admin/Views/page/edit.php
+++ b/themes/cp_admin/page/edit.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Page.edit') ?>
diff --git a/modules/Admin/Views/page/list.php b/themes/cp_admin/page/list.php
similarity index 97%
rename from modules/Admin/Views/page/list.php
rename to themes/cp_admin/page/list.php
index c3faf20cc0..a780e04210 100644
--- a/modules/Admin/Views/page/list.php
+++ b/themes/cp_admin/page/list.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Page.all_pages') ?>
diff --git a/modules/Admin/Views/page/view.php b/themes/cp_admin/page/view.php
similarity index 90%
rename from modules/Admin/Views/page/view.php
rename to themes/cp_admin/page/view.php
index 4d7a59d959..db28d587e8 100644
--- a/modules/Admin/Views/page/view.php
+++ b/themes/cp_admin/page/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $page->title ?>
diff --git a/modules/Admin/Views/person/create.php b/themes/cp_admin/person/create.php
similarity index 97%
rename from modules/Admin/Views/person/create.php
rename to themes/cp_admin/person/create.php
index 3458cf739b..4eee7be135 100644
--- a/modules/Admin/Views/person/create.php
+++ b/themes/cp_admin/person/create.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Person.create') ?>
diff --git a/modules/Admin/Views/person/edit.php b/themes/cp_admin/person/edit.php
similarity index 97%
rename from modules/Admin/Views/person/edit.php
rename to themes/cp_admin/person/edit.php
index bbca43db0c..e7bd273aa4 100644
--- a/modules/Admin/Views/person/edit.php
+++ b/themes/cp_admin/person/edit.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Person.edit') ?>
diff --git a/modules/Admin/Views/person/list.php b/themes/cp_admin/person/list.php
similarity index 97%
rename from modules/Admin/Views/person/list.php
rename to themes/cp_admin/person/list.php
index ef2a729955..0da3d289e8 100644
--- a/modules/Admin/Views/person/list.php
+++ b/themes/cp_admin/person/list.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Person.all_persons') ?>
diff --git a/modules/Admin/Views/person/view.php b/themes/cp_admin/person/view.php
similarity index 94%
rename from modules/Admin/Views/person/view.php
rename to themes/cp_admin/person/view.php
index 19a277ded8..94d7c9b7d1 100644
--- a/modules/Admin/Views/person/view.php
+++ b/themes/cp_admin/person/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $person->full_name ?>
diff --git a/modules/Admin/Views/podcast/_sidebar.php b/themes/cp_admin/podcast/_sidebar.php
similarity index 100%
rename from modules/Admin/Views/podcast/_sidebar.php
rename to themes/cp_admin/podcast/_sidebar.php
diff --git a/modules/Admin/Views/podcast/analytics/index.php b/themes/cp_admin/podcast/analytics/index.php
similarity index 95%
rename from modules/Admin/Views/podcast/analytics/index.php
rename to themes/cp_admin/podcast/analytics/index.php
index c80e79670a..028deac2e5 100644
--- a/modules/Admin/Views/podcast/analytics/index.php
+++ b/themes/cp_admin/podcast/analytics/index.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/analytics/listening_time.php b/themes/cp_admin/podcast/analytics/listening_time.php
similarity index 94%
rename from modules/Admin/Views/podcast/analytics/listening_time.php
rename to themes/cp_admin/podcast/analytics/listening_time.php
index 23a7f9896b..0a1c7b2960 100644
--- a/modules/Admin/Views/podcast/analytics/listening_time.php
+++ b/themes/cp_admin/podcast/analytics/listening_time.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/analytics/locations.php b/themes/cp_admin/podcast/analytics/locations.php
similarity index 96%
rename from modules/Admin/Views/podcast/analytics/locations.php
rename to themes/cp_admin/podcast/analytics/locations.php
index cb4c5e9709..05cb4fd8a4 100644
--- a/modules/Admin/Views/podcast/analytics/locations.php
+++ b/themes/cp_admin/podcast/analytics/locations.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/analytics/players.php b/themes/cp_admin/podcast/analytics/players.php
similarity index 97%
rename from modules/Admin/Views/podcast/analytics/players.php
rename to themes/cp_admin/podcast/analytics/players.php
index 6688d499cc..e76c9e3ac7 100644
--- a/modules/Admin/Views/podcast/analytics/players.php
+++ b/themes/cp_admin/podcast/analytics/players.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/analytics/time_periods.php b/themes/cp_admin/podcast/analytics/time_periods.php
similarity index 95%
rename from modules/Admin/Views/podcast/analytics/time_periods.php
rename to themes/cp_admin/podcast/analytics/time_periods.php
index aad964d0c8..7c8bef9180 100644
--- a/modules/Admin/Views/podcast/analytics/time_periods.php
+++ b/themes/cp_admin/podcast/analytics/time_periods.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/analytics/unique_listeners.php b/themes/cp_admin/podcast/analytics/unique_listeners.php
similarity index 94%
rename from modules/Admin/Views/podcast/analytics/unique_listeners.php
rename to themes/cp_admin/podcast/analytics/unique_listeners.php
index 9a2c01db96..5b6c4c2555 100644
--- a/modules/Admin/Views/podcast/analytics/unique_listeners.php
+++ b/themes/cp_admin/podcast/analytics/unique_listeners.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/analytics/webpages.php b/themes/cp_admin/podcast/analytics/webpages.php
similarity index 97%
rename from modules/Admin/Views/podcast/analytics/webpages.php
rename to themes/cp_admin/podcast/analytics/webpages.php
index befddd7217..58b2178f10 100644
--- a/modules/Admin/Views/podcast/analytics/webpages.php
+++ b/themes/cp_admin/podcast/analytics/webpages.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
diff --git a/modules/Admin/Views/podcast/create.php b/themes/cp_admin/podcast/create.php
similarity index 99%
rename from modules/Admin/Views/podcast/create.php
rename to themes/cp_admin/podcast/create.php
index ca5ec8f862..fbc893102f 100644
--- a/modules/Admin/Views/podcast/create.php
+++ b/themes/cp_admin/podcast/create.php
@@ -1,7 +1,7 @@
 <?php 
 ?>
 
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Podcast.create') ?>
diff --git a/modules/Admin/Views/podcast/edit.php b/themes/cp_admin/podcast/edit.php
similarity index 98%
rename from modules/Admin/Views/podcast/edit.php
rename to themes/cp_admin/podcast/edit.php
index bf21b9352b..618b6c47e5 100644
--- a/modules/Admin/Views/podcast/edit.php
+++ b/themes/cp_admin/podcast/edit.php
@@ -1,7 +1,7 @@
 <?php 
 ?>
 
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Podcast.edit') ?>
@@ -363,7 +363,7 @@ lang('Podcast.form.classification_section_subtitle'),
 
 <?= form_section_close() ?>
 
-<Button variant="primary" type="submit" class="self-end" iconLeft="heart">
+<Button variant="primary" type="submit" class="self-end">
 <?= lang('Podcast.form.submit_edit') ?>
 </Button>
 
diff --git a/modules/Admin/Views/podcast/import.php b/themes/cp_admin/podcast/import.php
similarity index 99%
rename from modules/Admin/Views/podcast/import.php
rename to themes/cp_admin/podcast/import.php
index f8740b7a3d..7d279a9304 100644
--- a/modules/Admin/Views/podcast/import.php
+++ b/themes/cp_admin/podcast/import.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Podcast.import') ?>
diff --git a/modules/Admin/Views/podcast/latest_episodes.php b/themes/cp_admin/podcast/latest_episodes.php
similarity index 100%
rename from modules/Admin/Views/podcast/latest_episodes.php
rename to themes/cp_admin/podcast/latest_episodes.php
diff --git a/modules/Admin/Views/podcast/list.php b/themes/cp_admin/podcast/list.php
similarity index 97%
rename from modules/Admin/Views/podcast/list.php
rename to themes/cp_admin/podcast/list.php
index f9ddce561d..728eca1cc2 100644
--- a/modules/Admin/Views/podcast/list.php
+++ b/themes/cp_admin/podcast/list.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Podcast.all_podcasts') ?>
diff --git a/modules/Admin/Views/podcast/persons.php b/themes/cp_admin/podcast/persons.php
similarity index 98%
rename from modules/Admin/Views/podcast/persons.php
rename to themes/cp_admin/podcast/persons.php
index 1f79dfdd3c..719e60200b 100644
--- a/modules/Admin/Views/podcast/persons.php
+++ b/themes/cp_admin/podcast/persons.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Person.podcast_form.title') ?>
diff --git a/modules/Admin/Views/podcast/platforms.php b/themes/cp_admin/podcast/platforms.php
similarity index 98%
rename from modules/Admin/Views/podcast/platforms.php
rename to themes/cp_admin/podcast/platforms.php
index 8d1ecf35c1..a63767851f 100644
--- a/modules/Admin/Views/podcast/platforms.php
+++ b/themes/cp_admin/podcast/platforms.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Platforms.title') ?>
diff --git a/modules/Admin/Views/podcast/settings/dashboard.php b/themes/cp_admin/podcast/settings/dashboard.php
similarity index 84%
rename from modules/Admin/Views/podcast/settings/dashboard.php
rename to themes/cp_admin/podcast/settings/dashboard.php
index ac35812f4a..87150c4f6d 100644
--- a/modules/Admin/Views/podcast/settings/dashboard.php
+++ b/themes/cp_admin/podcast/settings/dashboard.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('Podcast.platforms.title') ?>
diff --git a/modules/Admin/Views/podcast/view.php b/themes/cp_admin/podcast/view.php
similarity index 90%
rename from modules/Admin/Views/podcast/view.php
rename to themes/cp_admin/podcast/view.php
index dc3634c785..c8d9da56ca 100644
--- a/modules/Admin/Views/podcast/view.php
+++ b/themes/cp_admin/podcast/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= $podcast->title ?>
@@ -29,7 +29,7 @@
 
 <?= view_cell('Modules\Admin\Controllers\PodcastController::latestEpisodes', [
     'limit' => 5,
-    'podcast_id' => $podcast->id,
+    'podcastId' => $podcast->id,
 ]) ?>
 
 <?= $this->endSection() ?>
diff --git a/modules/Admin/Views/user/create.php b/themes/cp_admin/user/create.php
similarity index 95%
rename from modules/Admin/Views/user/create.php
rename to themes/cp_admin/user/create.php
index 7175b4330e..47967cc831 100644
--- a/modules/Admin/Views/user/create.php
+++ b/themes/cp_admin/user/create.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('User.create') ?>
diff --git a/modules/Admin/Views/user/edit.php b/themes/cp_admin/user/edit.php
similarity index 94%
rename from modules/Admin/Views/user/edit.php
rename to themes/cp_admin/user/edit.php
index 77a5d6a2f6..0db5f0e474 100644
--- a/modules/Admin/Views/user/edit.php
+++ b/themes/cp_admin/user/edit.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('User.edit_roles', ['username' => $user->username]) ?>
diff --git a/modules/Admin/Views/user/list.php b/themes/cp_admin/user/list.php
similarity index 98%
rename from modules/Admin/Views/user/list.php
rename to themes/cp_admin/user/list.php
index 2771199f77..3ae8e41ca9 100644
--- a/modules/Admin/Views/user/list.php
+++ b/themes/cp_admin/user/list.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('User.all_users') ?>
diff --git a/modules/Admin/Views/user/view.php b/themes/cp_admin/user/view.php
similarity index 58%
rename from modules/Admin/Views/user/view.php
rename to themes/cp_admin/user/view.php
index 462519ffd7..020bd587f9 100644
--- a/modules/Admin/Views/user/view.php
+++ b/themes/cp_admin/user/view.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Admin\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('title') ?>
 <?= lang('User.view', ['username' => $user->username]) ?>
@@ -7,6 +7,6 @@
 
 <?= $this->section('content') ?>
 
-<?= view('Modules\Admin\Views\_partials/_user_info.php', ['user' => $user]) ?>
+<?= view('_partials/_user_info.php', ['user' => $user]) ?>
 
 <?= $this->endSection() ?>
diff --git a/app/Views/_layout.php b/themes/cp_app/_layout.php
similarity index 100%
rename from app/Views/_layout.php
rename to themes/cp_app/_layout.php
diff --git a/app/Views/credits.php b/themes/cp_app/credits.php
similarity index 100%
rename from app/Views/credits.php
rename to themes/cp_app/credits.php
diff --git a/app/Views/embeddable_player.php b/themes/cp_app/embeddable_player.php
similarity index 100%
rename from app/Views/embeddable_player.php
rename to themes/cp_app/embeddable_player.php
diff --git a/app/Views/home.php b/themes/cp_app/home.php
similarity index 100%
rename from app/Views/home.php
rename to themes/cp_app/home.php
diff --git a/themes/cp_app/manifest.json b/themes/cp_app/manifest.json
new file mode 100644
index 0000000000..19282addb4
--- /dev/null
+++ b/themes/cp_app/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "Castopod App",
+  "description": "Castopod's default theme for app"
+}
diff --git a/app/Views/page.php b/themes/cp_app/page.php
similarity index 100%
rename from app/Views/page.php
rename to themes/cp_app/page.php
diff --git a/app/Views/podcast/_layout.php b/themes/cp_app/podcast/_layout.php
similarity index 100%
rename from app/Views/podcast/_layout.php
rename to themes/cp_app/podcast/_layout.php
diff --git a/app/Views/podcast/_layout_authenticated.php b/themes/cp_app/podcast/_layout_authenticated.php
similarity index 100%
rename from app/Views/podcast/_layout_authenticated.php
rename to themes/cp_app/podcast/_layout_authenticated.php
diff --git a/app/Views/podcast/_partials/comment.php b/themes/cp_app/podcast/_partials/comment.php
similarity index 100%
rename from app/Views/podcast/_partials/comment.php
rename to themes/cp_app/podcast/_partials/comment.php
diff --git a/app/Views/podcast/_partials/comment_actions.php b/themes/cp_app/podcast/_partials/comment_actions.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_actions.php
rename to themes/cp_app/podcast/_partials/comment_actions.php
diff --git a/app/Views/podcast/_partials/comment_actions_authenticated.php b/themes/cp_app/podcast/_partials/comment_actions_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_actions_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_actions_authenticated.php
diff --git a/app/Views/podcast/_partials/comment_actions_from_post.php b/themes/cp_app/podcast/_partials/comment_actions_from_post.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_actions_from_post.php
rename to themes/cp_app/podcast/_partials/comment_actions_from_post.php
diff --git a/app/Views/podcast/_partials/comment_actions_from_post_authenticated.php b/themes/cp_app/podcast/_partials/comment_actions_from_post_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_actions_from_post_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_actions_from_post_authenticated.php
diff --git a/app/Views/podcast/_partials/comment_authenticated.php b/themes/cp_app/podcast/_partials/comment_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_authenticated.php
diff --git a/app/Views/podcast/_partials/comment_card.php b/themes/cp_app/podcast/_partials/comment_card.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_card.php
rename to themes/cp_app/podcast/_partials/comment_card.php
diff --git a/app/Views/podcast/_partials/comment_card_authenticated.php b/themes/cp_app/podcast/_partials/comment_card_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_card_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_card_authenticated.php
diff --git a/app/Views/podcast/_partials/comment_reply.php b/themes/cp_app/podcast/_partials/comment_reply.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_reply.php
rename to themes/cp_app/podcast/_partials/comment_reply.php
diff --git a/app/Views/podcast/_partials/comment_reply_actions.php b/themes/cp_app/podcast/_partials/comment_reply_actions.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_reply_actions.php
rename to themes/cp_app/podcast/_partials/comment_reply_actions.php
diff --git a/app/Views/podcast/_partials/comment_reply_actions_authenticated.php b/themes/cp_app/podcast/_partials/comment_reply_actions_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_reply_actions_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_reply_actions_authenticated.php
diff --git a/app/Views/podcast/_partials/comment_reply_authenticated.php b/themes/cp_app/podcast/_partials/comment_reply_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_reply_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_reply_authenticated.php
diff --git a/app/Views/podcast/_partials/comment_with_replies.php b/themes/cp_app/podcast/_partials/comment_with_replies.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_with_replies.php
rename to themes/cp_app/podcast/_partials/comment_with_replies.php
diff --git a/app/Views/podcast/_partials/comment_with_replies_authenticated.php b/themes/cp_app/podcast/_partials/comment_with_replies_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/comment_with_replies_authenticated.php
rename to themes/cp_app/podcast/_partials/comment_with_replies_authenticated.php
diff --git a/app/Views/podcast/_partials/episode_card.php b/themes/cp_app/podcast/_partials/episode_card.php
similarity index 100%
rename from app/Views/podcast/_partials/episode_card.php
rename to themes/cp_app/podcast/_partials/episode_card.php
diff --git a/app/Views/podcast/_partials/episode_preview_card.php b/themes/cp_app/podcast/_partials/episode_preview_card.php
similarity index 100%
rename from app/Views/podcast/_partials/episode_preview_card.php
rename to themes/cp_app/podcast/_partials/episode_preview_card.php
diff --git a/app/Views/podcast/_partials/header.php b/themes/cp_app/podcast/_partials/header.php
similarity index 100%
rename from app/Views/podcast/_partials/header.php
rename to themes/cp_app/podcast/_partials/header.php
diff --git a/app/Views/podcast/_partials/post.php b/themes/cp_app/podcast/_partials/post.php
similarity index 100%
rename from app/Views/podcast/_partials/post.php
rename to themes/cp_app/podcast/_partials/post.php
diff --git a/app/Views/podcast/_partials/post_actions.php b/themes/cp_app/podcast/_partials/post_actions.php
similarity index 100%
rename from app/Views/podcast/_partials/post_actions.php
rename to themes/cp_app/podcast/_partials/post_actions.php
diff --git a/app/Views/podcast/_partials/post_actions_authenticated.php b/themes/cp_app/podcast/_partials/post_actions_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/post_actions_authenticated.php
rename to themes/cp_app/podcast/_partials/post_actions_authenticated.php
diff --git a/app/Views/podcast/_partials/post_authenticated.php b/themes/cp_app/podcast/_partials/post_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/post_authenticated.php
rename to themes/cp_app/podcast/_partials/post_authenticated.php
diff --git a/app/Views/podcast/_partials/post_with_replies.php b/themes/cp_app/podcast/_partials/post_with_replies.php
similarity index 100%
rename from app/Views/podcast/_partials/post_with_replies.php
rename to themes/cp_app/podcast/_partials/post_with_replies.php
diff --git a/app/Views/podcast/_partials/post_with_replies_authenticated.php b/themes/cp_app/podcast/_partials/post_with_replies_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/post_with_replies_authenticated.php
rename to themes/cp_app/podcast/_partials/post_with_replies_authenticated.php
diff --git a/app/Views/podcast/_partials/preview_card.php b/themes/cp_app/podcast/_partials/preview_card.php
similarity index 100%
rename from app/Views/podcast/_partials/preview_card.php
rename to themes/cp_app/podcast/_partials/preview_card.php
diff --git a/app/Views/podcast/_partials/reblog.php b/themes/cp_app/podcast/_partials/reblog.php
similarity index 100%
rename from app/Views/podcast/_partials/reblog.php
rename to themes/cp_app/podcast/_partials/reblog.php
diff --git a/app/Views/podcast/_partials/reblog_authenticated.php b/themes/cp_app/podcast/_partials/reblog_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/reblog_authenticated.php
rename to themes/cp_app/podcast/_partials/reblog_authenticated.php
diff --git a/app/Views/podcast/_partials/reply.php b/themes/cp_app/podcast/_partials/reply.php
similarity index 100%
rename from app/Views/podcast/_partials/reply.php
rename to themes/cp_app/podcast/_partials/reply.php
diff --git a/app/Views/podcast/_partials/reply_actions.php b/themes/cp_app/podcast/_partials/reply_actions.php
similarity index 100%
rename from app/Views/podcast/_partials/reply_actions.php
rename to themes/cp_app/podcast/_partials/reply_actions.php
diff --git a/app/Views/podcast/_partials/reply_actions_authenticated.php b/themes/cp_app/podcast/_partials/reply_actions_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/reply_actions_authenticated.php
rename to themes/cp_app/podcast/_partials/reply_actions_authenticated.php
diff --git a/app/Views/podcast/_partials/reply_authenticated.php b/themes/cp_app/podcast/_partials/reply_authenticated.php
similarity index 100%
rename from app/Views/podcast/_partials/reply_authenticated.php
rename to themes/cp_app/podcast/_partials/reply_authenticated.php
diff --git a/app/Views/podcast/_partials/sidebar.php b/themes/cp_app/podcast/_partials/sidebar.php
similarity index 100%
rename from app/Views/podcast/_partials/sidebar.php
rename to themes/cp_app/podcast/_partials/sidebar.php
diff --git a/app/Views/podcast/activity.php b/themes/cp_app/podcast/activity.php
similarity index 100%
rename from app/Views/podcast/activity.php
rename to themes/cp_app/podcast/activity.php
diff --git a/app/Views/podcast/activity_authenticated.php b/themes/cp_app/podcast/activity_authenticated.php
similarity index 100%
rename from app/Views/podcast/activity_authenticated.php
rename to themes/cp_app/podcast/activity_authenticated.php
diff --git a/app/Views/podcast/comment.php b/themes/cp_app/podcast/comment.php
similarity index 100%
rename from app/Views/podcast/comment.php
rename to themes/cp_app/podcast/comment.php
diff --git a/app/Views/podcast/comment_authenticated.php b/themes/cp_app/podcast/comment_authenticated.php
similarity index 100%
rename from app/Views/podcast/comment_authenticated.php
rename to themes/cp_app/podcast/comment_authenticated.php
diff --git a/app/Views/podcast/episode.php b/themes/cp_app/podcast/episode.php
similarity index 100%
rename from app/Views/podcast/episode.php
rename to themes/cp_app/podcast/episode.php
diff --git a/app/Views/podcast/episode_authenticated.php b/themes/cp_app/podcast/episode_authenticated.php
similarity index 100%
rename from app/Views/podcast/episode_authenticated.php
rename to themes/cp_app/podcast/episode_authenticated.php
diff --git a/app/Views/podcast/episodes.php b/themes/cp_app/podcast/episodes.php
similarity index 99%
rename from app/Views/podcast/episodes.php
rename to themes/cp_app/podcast/episodes.php
index ca97759f67..1cc3d7becc 100644
--- a/app/Views/podcast/episodes.php
+++ b/themes/cp_app/podcast/episodes.php
@@ -83,6 +83,7 @@
         <?php foreach ($episodes as $episode): ?>
             <?= view('podcast/_partials/episode_card', [
                 'episode' => $episode,
+                'podcast' => $podcast
             ]) ?>
         <?php endforeach; ?>
     <?php else: ?>
diff --git a/app/Views/podcast/episodes_authenticated.php b/themes/cp_app/podcast/episodes_authenticated.php
similarity index 99%
rename from app/Views/podcast/episodes_authenticated.php
rename to themes/cp_app/podcast/episodes_authenticated.php
index 09b38ede83..ade11b0eeb 100644
--- a/app/Views/podcast/episodes_authenticated.php
+++ b/themes/cp_app/podcast/episodes_authenticated.php
@@ -83,6 +83,7 @@
         <?php foreach ($episodes as $episode) : ?>
             <?= view('podcast/_partials/episode_card', [
                 'episode' => $episode,
+                'podcast' => $podcast
             ]) ?>
         <?php endforeach; ?>
     <?php else : ?>
diff --git a/app/Views/podcast/follow.php b/themes/cp_app/podcast/follow.php
similarity index 100%
rename from app/Views/podcast/follow.php
rename to themes/cp_app/podcast/follow.php
diff --git a/app/Views/podcast/post.php b/themes/cp_app/podcast/post.php
similarity index 100%
rename from app/Views/podcast/post.php
rename to themes/cp_app/podcast/post.php
diff --git a/app/Views/podcast/post_authenticated.php b/themes/cp_app/podcast/post_authenticated.php
similarity index 100%
rename from app/Views/podcast/post_authenticated.php
rename to themes/cp_app/podcast/post_authenticated.php
diff --git a/app/Views/podcast/post_remote_action.php b/themes/cp_app/podcast/post_remote_action.php
similarity index 100%
rename from app/Views/podcast/post_remote_action.php
rename to themes/cp_app/podcast/post_remote_action.php
diff --git a/modules/Auth/Views/_layout.php b/themes/cp_auth/_layout.php
similarity index 97%
rename from modules/Auth/Views/_layout.php
rename to themes/cp_auth/_layout.php
index f16abcea6e..0fe7835bed 100644
--- a/modules/Auth/Views/_layout.php
+++ b/themes/cp_auth/_layout.php
@@ -23,7 +23,7 @@
 		<h1 class="mb-6 text-2xl font-bold text-center font-display"><?= $this->renderSection(
       'title',
   ) ?></h1>
-		<?= view('_message_block') ?>
+		<!-- view('_message_block') -->
 		<?= $this->renderSection('content') ?>
 	</main>
 	<footer class="flex flex-col text-sm">
diff --git a/modules/Auth/Views/emails/activation.php b/themes/cp_auth/emails/activation.php
similarity index 100%
rename from modules/Auth/Views/emails/activation.php
rename to themes/cp_auth/emails/activation.php
diff --git a/modules/Auth/Views/emails/forgot.php b/themes/cp_auth/emails/forgot.php
similarity index 100%
rename from modules/Auth/Views/emails/forgot.php
rename to themes/cp_auth/emails/forgot.php
diff --git a/modules/Auth/Views/forgot.php b/themes/cp_auth/forgot.php
similarity index 100%
rename from modules/Auth/Views/forgot.php
rename to themes/cp_auth/forgot.php
diff --git a/modules/Auth/Views/login.php b/themes/cp_auth/login.php
similarity index 100%
rename from modules/Auth/Views/login.php
rename to themes/cp_auth/login.php
diff --git a/themes/cp_auth/manifest.json b/themes/cp_auth/manifest.json
new file mode 100644
index 0000000000..917784f990
--- /dev/null
+++ b/themes/cp_auth/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "Castopod Auth",
+  "description": "Castopod's default theme for authentication"
+}
diff --git a/modules/Auth/Views/register.php b/themes/cp_auth/register.php
similarity index 100%
rename from modules/Auth/Views/register.php
rename to themes/cp_auth/register.php
diff --git a/modules/Auth/Views/reset.php b/themes/cp_auth/reset.php
similarity index 100%
rename from modules/Auth/Views/reset.php
rename to themes/cp_auth/reset.php
diff --git a/modules/Install/Views/_layout.php b/themes/cp_install/_layout.php
similarity index 96%
rename from modules/Install/Views/_layout.php
rename to themes/cp_install/_layout.php
index 91920f08dc..65fbd75d83 100644
--- a/modules/Install/Views/_layout.php
+++ b/themes/cp_install/_layout.php
@@ -18,7 +18,7 @@
         </div>
     </header>
     <main class="container flex flex-col items-center justify-center flex-1 px-4 py-10 mx-auto">
-        <?= view('_message_block') ?>
+        <!-- view('_message_block') -->
         <?= $this->renderSection('content') ?>
     </main>
     <footer class="container px-2 py-4 mx-auto text-sm text-right border-t">
diff --git a/modules/Install/Views/cache_config.php b/themes/cp_install/cache_config.php
similarity index 95%
rename from modules/Install/Views/cache_config.php
rename to themes/cp_install/cache_config.php
index 4c15821579..dd936031f7 100644
--- a/modules/Install/Views/cache_config.php
+++ b/themes/cp_install/cache_config.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Install\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('content') ?>
 
diff --git a/modules/Install/Views/create_superadmin.php b/themes/cp_install/create_superadmin.php
similarity index 96%
rename from modules/Install/Views/create_superadmin.php
rename to themes/cp_install/create_superadmin.php
index 3af6084cdc..22e44ccc76 100644
--- a/modules/Install/Views/create_superadmin.php
+++ b/themes/cp_install/create_superadmin.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Install\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('content') ?>
 
diff --git a/modules/Install/Views/database_config.php b/themes/cp_install/database_config.php
similarity index 97%
rename from modules/Install/Views/database_config.php
rename to themes/cp_install/database_config.php
index 9dbb083941..ab70b75958 100644
--- a/modules/Install/Views/database_config.php
+++ b/themes/cp_install/database_config.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Install\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('content') ?>
 
diff --git a/modules/Install/Views/instance_config.php b/themes/cp_install/instance_config.php
similarity index 97%
rename from modules/Install/Views/instance_config.php
rename to themes/cp_install/instance_config.php
index d8a000327d..ad5ec4cc1b 100644
--- a/modules/Install/Views/instance_config.php
+++ b/themes/cp_install/instance_config.php
@@ -1,7 +1,7 @@
-<?= $this->extend('Modules\Install\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('content') ?>
-
+adz
 <form action="<?= '/' .
     config('Install')->gateway .
     '/instance-config' ?>" class="flex flex-col w-full max-w-sm" method="post" accept-charset="utf-8">
diff --git a/themes/cp_install/manifest.json b/themes/cp_install/manifest.json
new file mode 100644
index 0000000000..fb454c1787
--- /dev/null
+++ b/themes/cp_install/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "Castopod Install",
+  "description": "Castopod's default theme for install wizard"
+}
diff --git a/modules/Install/Views/manual_config.php b/themes/cp_install/manual_config.php
similarity index 89%
rename from modules/Install/Views/manual_config.php
rename to themes/cp_install/manual_config.php
index 3ed77c74e6..ff5468e4b0 100644
--- a/modules/Install/Views/manual_config.php
+++ b/themes/cp_install/manual_config.php
@@ -1,4 +1,4 @@
-<?= $this->extend('Modules\Install\Views\_layout') ?>
+<?= $this->extend('_layout') ?>
 
 <?= $this->section('content') ?>
 
-- 
GitLab