From 5c56f3e6f00a61af2ccf50811c155c325f2b10fa Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Tue, 26 Oct 2021 15:54:56 +0000
Subject: [PATCH] feat(settings): add general config for instance (site name,
 description and icon)

---
 .gitignore                                    |   2 +
 app/Config/App.php                            |  20 +++
 app/Config/Routes.php                         |   6 +-
 app/Controllers/WebmanifestController.php     |  45 ++++++
 app/Database/Seeds/AuthSeeder.php             |  12 ++
 app/Helpers/media_helper.php                  |   3 +
 composer.json                                 |   8 +-
 composer.lock                                 | 153 ++++++++++++++----
 ecs.php                                       |   1 -
 modules/Admin/Config/Routes.php               |  15 ++
 .../Admin/Controllers/SettingsController.php  | 105 ++++++++++++
 modules/Admin/Language/en/AdminNavigation.php |   2 +
 modules/Admin/Language/en/Breadcrumb.php      |   1 +
 modules/Admin/Language/en/Settings.php        |  23 +++
 modules/Admin/Language/fr/AdminNavigation.php |   2 +
 modules/Admin/Language/fr/Settings.php        |  24 +++
 .../Install/Controllers/InstallController.php |   2 +
 public/favicon.ico                            | Bin 16958 -> 21238 bytes
 public/icon-180.png                           | Bin 0 -> 17522 bytes
 public/icon-192.png                           | Bin 0 -> 5913 bytes
 public/icon-512.png                           | Bin 0 -> 7340 bytes
 public/icon-64.png                            | Bin 0 -> 738 bytes
 public/icon.png                               | Bin 0 -> 7027 bytes
 themes/cp_admin/_layout.php                   |   5 +-
 themes/cp_admin/_sidebar.php                  |   4 +
 themes/cp_admin/person/edit.php               |   1 +
 themes/cp_admin/settings/general.php          |  59 +++++++
 themes/cp_app/_layout.php                     |  15 +-
 themes/cp_app/embed.php                       |   5 +-
 themes/cp_app/episode/_layout.php             |   5 +-
 themes/cp_app/home.php                        |  19 ++-
 themes/cp_app/map.php                         |   5 +-
 themes/cp_app/page.php                        |   5 +-
 themes/cp_app/podcast/_layout.php             |   5 +-
 themes/cp_app/podcast/about.php               |   5 +-
 themes/cp_app/podcast/activity.php            |   5 +-
 themes/cp_app/podcast/episodes.php            |   5 +-
 themes/cp_app/podcast/follow.php              |   6 +-
 themes/cp_app/post/remote_action.php          |   5 +-
 themes/cp_auth/_layout.php                    |   2 +-
 themes/cp_install/_layout.php                 |   5 +-
 41 files changed, 533 insertions(+), 52 deletions(-)
 create mode 100644 app/Controllers/WebmanifestController.php
 create mode 100644 modules/Admin/Controllers/SettingsController.php
 create mode 100644 modules/Admin/Language/en/Settings.php
 create mode 100644 modules/Admin/Language/fr/Settings.php
 create mode 100644 public/icon-180.png
 create mode 100644 public/icon-192.png
 create mode 100644 public/icon-512.png
 create mode 100644 public/icon-64.png
 create mode 100644 public/icon.png
 create mode 100644 themes/cp_admin/settings/general.php

diff --git a/.gitignore b/.gitignore
index 47f4c37308..4ed935bc0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -137,9 +137,11 @@ node_modules
 
 # public folder
 public/*
+public/media/site
 !public/media
 !public/.htaccess
 !public/favicon.ico
+!public/icon*
 !public/index.php
 !public/robots.txt
 
diff --git a/app/Config/App.php b/app/Config/App.php
index df445db0ed..4d2227c087 100644
--- a/app/Config/App.php
+++ b/app/Config/App.php
@@ -427,4 +427,24 @@ class App extends BaseConfig
      * Defines the root folder for media files storage
      */
     public string $mediaRoot = 'media';
+
+    /**
+     * --------------------------------------------------------------------------
+     * Instance / Site Config
+     * --------------------------------------------------------------------------
+     */
+    public string $siteName = 'Castopod';
+
+    public string $siteDescription = 'Castopod Host is an open-source hosting platform made for podcasters who want engage and interact with their audience.';
+
+    /**
+     * @var array<int|string, string>
+     */
+    public array $siteIcon = [
+        'ico' => '/favicon.ico',
+        '64' => '/icon-64.png',
+        '180' => '/icon-180.png',
+        '192' => '/icon-192.png',
+        '512' => '/icon-512.png',
+    ];
 }
diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 45eff6b951..7d2ca19f73 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -48,9 +48,13 @@ $routes->addPlaceholder(
  * --------------------------------------------------------------------
  */
 
+$routes->get('manifest.webmanifest', 'WebmanifestController', [
+    'as' => 'webmanifest',
+]);
+
 // We get a performance increase by specifying the default
 // route since we don't have to scan directories.
-$routes->get('/', 'HomeController::index', [
+$routes->get('/', 'HomeController', [
     'as' => 'home',
 ]);
 
diff --git a/app/Controllers/WebmanifestController.php b/app/Controllers/WebmanifestController.php
new file mode 100644
index 0000000000..dd320e440e
--- /dev/null
+++ b/app/Controllers/WebmanifestController.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright  2020 Podlibre
+ * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
+ * @link       https://castopod.org/
+ */
+
+namespace App\Controllers;
+
+use CodeIgniter\Controller;
+use CodeIgniter\HTTP\ResponseInterface;
+
+class WebmanifestController extends Controller
+{
+    public function index(): ResponseInterface
+    {
+        $webmanifest = [
+            'name' => service('settings')
+                ->get('App.siteName'),
+            'description' => service('settings')
+                ->get('App.siteDescription'),
+            'display' => 'minimal-ui',
+            'theme_color' => '#009486',
+            'icons' => [
+                [
+                    'src' => service('settings')
+                        ->get('App.siteIcon')['192'],
+                    'type' => 'image/png',
+                    'sizes' => '192x192',
+                ],
+                [
+                    'src' => service('settings')
+                        ->get('App.siteIcon')['512'],
+                    'type' => 'image/png',
+                    'sizes' => '512x512',
+                ],
+            ],
+        ];
+
+        return $this->response->setJSON($webmanifest);
+    }
+}
diff --git a/app/Database/Seeds/AuthSeeder.php b/app/Database/Seeds/AuthSeeder.php
index 2fb6fb6a28..eb9af6a760 100644
--- a/app/Database/Seeds/AuthSeeder.php
+++ b/app/Database/Seeds/AuthSeeder.php
@@ -40,6 +40,18 @@ class AuthSeeder extends Seeder
      * @var array<string, array<string, string|array>[]>
      */
     protected array $permissions = [
+        'settings' => [
+            [
+                'name' => 'view',
+                'description' => 'View settings options',
+                'has_permission' => ['superadmin'],
+            ],
+            [
+                'name' => 'manage',
+                'description' => 'Update general settings',
+                'has_permission' => ['superadmin'],
+            ],
+        ],
         'users' => [
             [
                 'name' => 'create',
diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php
index 312af42a3b..b9a1c65bf7 100644
--- a/app/Helpers/media_helper.php
+++ b/app/Helpers/media_helper.php
@@ -29,6 +29,9 @@ if (! function_exists('save_media')) {
 
         if (! file_exists($mediaRoot)) {
             mkdir($mediaRoot, 0777, true);
+        }
+
+        if (! file_exists($mediaRoot . '/index.html')) {
             touch($mediaRoot . '/index.html');
         }
 
diff --git a/composer.json b/composer.json
index 3ca24a4fc5..b7dffcaf40 100644
--- a/composer.json
+++ b/composer.json
@@ -12,15 +12,17 @@
     "geoip2/geoip2": "^v2.11.0",
     "myth/auth": "dev-develop",
     "codeigniter4/codeigniter4": "dev-develop",
-    "league/commonmark": "^1.6.6",
+    "league/commonmark": "^v1.6.6",
     "vlucas/phpdotenv": "^v5.3.0",
-    "league/html-to-markdown": "^5.0.0",
+    "league/html-to-markdown": "^v5.0.1",
     "opawg/user-agents-php": "^v1.0",
     "podlibre/ipcat": "^v1.0",
     "podlibre/podcast-namespace": "^v1.0.6",
     "phpseclib/phpseclib": "~2.0.30",
     "michalsn/codeigniter4-uuid": "dev-develop",
-    "essence/essence": "^3.5.4"
+    "essence/essence": "^3.5.4",
+    "codeigniter4/settings": "dev-develop",
+    "chrisjean/php-ico": "^1.0"
   },
   "require-dev": {
     "mikey179/vfsstream": "^v1.6.8",
diff --git a/composer.lock b/composer.lock
index 03baaec32b..82b1287a71 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
     "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
     "This file is @generated automatically"
   ],
-  "content-hash": "af4a438816f7adbbd6950d93ed333e9f",
+  "content-hash": "f35a050323bdc632cd550f9d13f0679c",
   "packages": [
     {
       "name": "brick/math",
@@ -60,6 +60,46 @@
       ],
       "time": "2021-01-20T22:51:39+00:00"
     },
+    {
+      "name": "chrisjean/php-ico",
+      "version": "1.0.4",
+      "source": {
+        "type": "git",
+        "url": "https://github.com/chrisbliss18/php-ico.git",
+        "reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6"
+      },
+      "dist": {
+        "type": "zip",
+        "url": "https://api.github.com/repos/chrisbliss18/php-ico/zipball/ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
+        "reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
+        "shasum": ""
+      },
+      "require": {
+        "ext-gd": "*",
+        "php": ">=5.2.4"
+      },
+      "type": "library",
+      "autoload": {
+        "classmap": ["class-php-ico.php"]
+      },
+      "notification-url": "https://packagist.org/downloads/",
+      "license": ["GPL-2.0+"],
+      "authors": [
+        {
+          "name": "Chris Jean",
+          "homepage": "https://chrisjean.com",
+          "role": "Developer"
+        }
+      ],
+      "description": "An easy-to-use library to generate valid ICO files.",
+      "homepage": "https://github.com/chrisbliss18/php-ico",
+      "keywords": ["favicon", "ico"],
+      "support": {
+        "issues": "https://github.com/chrisbliss18/php-ico/issues",
+        "source": "https://github.com/chrisbliss18/php-ico"
+      },
+      "time": "2016-09-27T22:00:56+00:00"
+    },
     {
       "name": "codeigniter4/codeigniter4",
       "version": "dev-develop",
@@ -137,6 +177,59 @@
       },
       "time": "2021-06-10T06:40:05+00:00"
     },
+    {
+      "name": "codeigniter4/settings",
+      "version": "dev-develop",
+      "source": {
+        "type": "git",
+        "url": "https://github.com/codeigniter4/settings.git",
+        "reference": "5d758e5e0a3f9dda9f66303d82ccfbb82e979577"
+      },
+      "dist": {
+        "type": "zip",
+        "url": "https://api.github.com/repos/codeigniter4/settings/zipball/5d758e5e0a3f9dda9f66303d82ccfbb82e979577",
+        "reference": "5d758e5e0a3f9dda9f66303d82ccfbb82e979577",
+        "shasum": ""
+      },
+      "require": {
+        "php": "^7.3 || ^8.0"
+      },
+      "require-dev": {
+        "codeigniter4/codeigniter4": "dev-develop",
+        "fakerphp/faker": "^1.9",
+        "mockery/mockery": "^1.0",
+        "nexusphp/tachycardia": "^1.0",
+        "php-coveralls/php-coveralls": "^2.4",
+        "phpstan/phpstan": "^0.12",
+        "phpunit/phpunit": "^9.0",
+        "squizlabs/php_codesniffer": "^3.3"
+      },
+      "default-branch": true,
+      "type": "library",
+      "autoload": {
+        "psr-4": {
+          "Sparks\\Settings\\": "src"
+        },
+        "exclude-from-classmap": ["**/Database/Migrations/**"]
+      },
+      "notification-url": "https://packagist.org/downloads/",
+      "license": ["MIT"],
+      "authors": [
+        {
+          "name": "Lonnie Ezell",
+          "email": "lonnieje@gmail.com",
+          "role": "Developer"
+        }
+      ],
+      "description": "Settings library for CodeIgniter 4",
+      "homepage": "https://github.com/codeigniter4/settings",
+      "keywords": ["Settings", "codeigniter", "codeigniter4"],
+      "support": {
+        "issues": "https://github.com/codeigniter4/settings/issues",
+        "source": "https://github.com/codeigniter4/settings/tree/develop"
+      },
+      "time": "2021-08-16T05:07:59+00:00"
+    },
     {
       "name": "composer/ca-bundle",
       "version": "1.2.10",
@@ -494,16 +587,16 @@
     },
     {
       "name": "james-heinrich/getid3",
-      "version": "2.0.x-dev",
+      "version": "v2.0.0-beta4",
       "source": {
         "type": "git",
         "url": "https://github.com/JamesHeinrich/getID3.git",
-        "reference": "ee238d552571c6029898b087d5fc95df826418d6"
+        "reference": "5ad79104e937e7d9c8a9141a97e1f063dd1123f8"
       },
       "dist": {
         "type": "zip",
-        "url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/ee238d552571c6029898b087d5fc95df826418d6",
-        "reference": "ee238d552571c6029898b087d5fc95df826418d6",
+        "url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/5ad79104e937e7d9c8a9141a97e1f063dd1123f8",
+        "reference": "5ad79104e937e7d9c8a9141a97e1f063dd1123f8",
         "shasum": ""
       },
       "require": {
@@ -562,9 +655,9 @@
       "keywords": ["audio", "codecs", "id3", "metadata", "tags", "video"],
       "support": {
         "issues": "https://github.com/JamesHeinrich/getID3/issues",
-        "source": "https://github.com/JamesHeinrich/getID3/tree/2.0"
+        "source": "https://github.com/JamesHeinrich/getID3/tree/v2.0.0-beta4"
       },
-      "time": "2021-12-15T17:29:14+00:00"
+      "time": "2021-10-06T16:23:45+00:00"
     },
     {
       "name": "kint-php/kint",
@@ -745,16 +838,16 @@
     },
     {
       "name": "league/commonmark",
-      "version": "1.6.2",
+      "version": "1.6.6",
       "source": {
         "type": "git",
         "url": "https://github.com/thephpleague/commonmark.git",
-        "reference": "7d70d2f19c84bcc16275ea47edabee24747352eb"
+        "reference": "c4228d11e30d7493c6836d20872f9582d8ba6dcf"
       },
       "dist": {
         "type": "zip",
-        "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/7d70d2f19c84bcc16275ea47edabee24747352eb",
-        "reference": "7d70d2f19c84bcc16275ea47edabee24747352eb",
+        "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c4228d11e30d7493c6836d20872f9582d8ba6dcf",
+        "reference": "c4228d11e30d7493c6836d20872f9582d8ba6dcf",
         "shasum": ""
       },
       "require": {
@@ -772,7 +865,7 @@
         "github/gfm": "0.29.0",
         "michelf/php-markdown": "~1.4",
         "mikehaertl/php-shellcommand": "^1.4",
-        "phpstan/phpstan": "^0.12",
+        "phpstan/phpstan": "^0.12.90",
         "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
         "scrutinizer/ocular": "^1.5",
         "symfony/finder": "^4.2"
@@ -838,37 +931,40 @@
           "type": "tidelift"
         }
       ],
-      "time": "2021-05-12T11:39:41+00:00"
+      "time": "2021-07-17T17:13:23+00:00"
     },
     {
       "name": "league/html-to-markdown",
-      "version": "4.10.0",
+      "version": "5.0.1",
       "source": {
         "type": "git",
         "url": "https://github.com/thephpleague/html-to-markdown.git",
-        "reference": "0868ae7a552e809e5cd8f93ba022071640408e88"
+        "reference": "e5600a2c5ce7b7571b16732c7086940f56f7abec"
       },
       "dist": {
         "type": "zip",
-        "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0868ae7a552e809e5cd8f93ba022071640408e88",
-        "reference": "0868ae7a552e809e5cd8f93ba022071640408e88",
+        "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e5600a2c5ce7b7571b16732c7086940f56f7abec",
+        "reference": "e5600a2c5ce7b7571b16732c7086940f56f7abec",
         "shasum": ""
       },
       "require": {
         "ext-dom": "*",
         "ext-xml": "*",
-        "php": ">=5.3.3"
+        "php": "^7.2.5 || ^8.0"
       },
       "require-dev": {
-        "mikehaertl/php-shellcommand": "~1.1.0",
-        "phpunit/phpunit": "^4.8|^5.7",
-        "scrutinizer/ocular": "~1.1"
+        "mikehaertl/php-shellcommand": "^1.1.0",
+        "phpstan/phpstan": "^0.12.82",
+        "phpunit/phpunit": "^8.5 || ^9.2",
+        "scrutinizer/ocular": "^1.6",
+        "unleashedtech/php-coding-standard": "^2.7",
+        "vimeo/psalm": "^4.6"
       },
       "bin": ["bin/html-to-markdown"],
       "type": "library",
       "extra": {
         "branch-alias": {
-          "dev-master": "4.10-dev"
+          "dev-master": "5.1-dev"
         }
       },
       "autoload": {
@@ -897,7 +993,7 @@
       "keywords": ["html", "markdown"],
       "support": {
         "issues": "https://github.com/thephpleague/html-to-markdown/issues",
-        "source": "https://github.com/thephpleague/html-to-markdown/tree/4.10.0"
+        "source": "https://github.com/thephpleague/html-to-markdown/tree/5.0.1"
       },
       "funding": [
         {
@@ -913,11 +1009,11 @@
           "type": "github"
         },
         {
-          "url": "https://www.patreon.com/colinodell",
-          "type": "patreon"
+          "url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown",
+          "type": "tidelift"
         }
       ],
-      "time": "2020-07-01T00:34:03+00:00"
+      "time": "2021-09-17T20:00:27+00:00"
     },
     {
       "name": "maxmind-db/reader",
@@ -7457,7 +7553,8 @@
     "james-heinrich/getid3": 20,
     "myth/auth": 20,
     "codeigniter4/codeigniter4": 20,
-    "michalsn/codeigniter4-uuid": 20
+    "michalsn/codeigniter4-uuid": 20,
+    "codeigniter4/settings": 20
   },
   "prefer-stable": true,
   "prefer-lowest": false,
@@ -7465,5 +7562,5 @@
     "php": "^8.0"
   },
   "platform-dev": [],
-  "plugin-api-version": "2.0.0"
+  "plugin-api-version": "2.1.0"
 }
diff --git a/ecs.php b/ecs.php
index 939cae297d..a7d4071516 100644
--- a/ecs.php
+++ b/ecs.php
@@ -17,7 +17,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
         __DIR__ . '/themes',
         __DIR__ . '/tests',
         __DIR__ . '/public',
-        __DIR__ . '/public',
     ]);
 
     $parameters->set(Option::SKIP, [
diff --git a/modules/Admin/Config/Routes.php b/modules/Admin/Config/Routes.php
index d259ce905c..edda5e8152 100644
--- a/modules/Admin/Config/Routes.php
+++ b/modules/Admin/Config/Routes.php
@@ -18,6 +18,21 @@ $routes->group(
             'as' => 'admin',
         ]);
 
+        $routes->group('settings', function ($routes): void {
+            $routes->get('/', 'SettingsController', [
+                'as' => 'settings-general',
+                'filter' => 'permission:settings-manage',
+            ]);
+            $routes->post('instance', 'SettingsController::attemptInstanceEdit', [
+                'as' => 'settings-instance',
+                'filter' => 'permission:settings-manage',
+            ]);
+            $routes->get('instance-delete-icon', 'SettingsController::deleteIcon', [
+                'as' => 'settings-instance-delete-icon',
+                'filter' => 'permission:settings-manage',
+            ]);
+        });
+
         $routes->group('persons', function ($routes): void {
             $routes->get('/', 'PersonController', [
                 'as' => 'person-list',
diff --git a/modules/Admin/Controllers/SettingsController.php b/modules/Admin/Controllers/SettingsController.php
new file mode 100644
index 0000000000..1308df71f5
--- /dev/null
+++ b/modules/Admin/Controllers/SettingsController.php
@@ -0,0 +1,105 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright  2020 Podlibre
+ * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
+ * @link       https://castopod.org/
+ */
+
+namespace Modules\Admin\Controllers;
+
+use CodeIgniter\HTTP\RedirectResponse;
+use PHP_ICO;
+
+class SettingsController extends BaseController
+{
+    public function index(): string
+    {
+        helper('form');
+        return view('settings/general');
+    }
+
+    public function attemptInstanceEdit(): RedirectResponse
+    {
+        $rules = [
+            'site_icon' =>
+                'is_image[site_icon]|ext_in[site_icon,png,jpeg]|is_image_squared[site_icon]|min_dims[image,512,512]|permit_empty',
+        ];
+
+        if (! $this->validate($rules)) {
+            return redirect()
+                ->back()
+                ->withInput()
+                ->with('errors', $this->validator->getErrors());
+        }
+
+        $siteName = $this->request->getPost('site_name');
+        if ($siteName !== service('settings')->get('App.siteName')) {
+            service('settings')->set('App.siteName', $siteName);
+        }
+
+        $siteDescription = $this->request->getPost('site_description');
+        if ($siteDescription !== service('settings')->get('App.siteDescription')) {
+            service('settings')->set('App.siteDescription', $siteDescription);
+        }
+
+        $siteIconFile = $this->request->getFile('site_icon');
+        if ($siteIconFile !== null && $siteIconFile->isValid()) {
+            helper(['filesystem', 'media']);
+
+            // delete site folder in media before repopulating it
+            delete_files(ROOTPATH . 'public/media/site/');
+
+            // save original in disk
+            $originalFilename = save_media($siteIconFile, 'site', 'icon');
+
+            // convert jpeg image to png if not
+            if ($siteIconFile->getClientMimeType() !== 'image/png') {
+                service('image')->withFile(ROOTPATH . 'public/media/' . $originalFilename)
+                    ->convert(IMAGETYPE_JPEG)
+                    ->save(ROOTPATH . 'public/media/site/icon.png');
+            }
+
+            // generate random hash to use as a suffix to renew browser cache
+            $randomHash = substr(bin2hex(random_bytes(18)), 0, 8);
+
+            // generate ico
+            $ico_lib = new PHP_ICO();
+            $ico_lib->add_image(ROOTPATH . 'public/media/site/icon.png', [[32, 32], [64, 64]]);
+            $ico_lib->save_ico(ROOTPATH . "public/media/site/favicon.{$randomHash}.ico");
+
+            // resize original to needed sizes
+            foreach ([64, 180, 192, 512] as $size) {
+                service('image')
+                    ->withFile(ROOTPATH . 'public/media/site/icon.png')
+                    ->resize($size, $size)
+                    ->save(ROOTPATH . "public/media/site/icon-{$size}.{$randomHash}.png");
+            }
+
+            service('settings')
+                ->set('App.siteIcon', [
+                    'ico' => "/media/site/favicon.{$randomHash}.ico",
+                    '64' => "/media/site/icon-64.{$randomHash}.png",
+                    '180' => "/media/site/icon-180.{$randomHash}.png",
+                    '192' => "/media/site/icon-192.{$randomHash}.png",
+                    '512' => "/media/site/icon-512.{$randomHash}.png",
+                ]);
+        }
+
+        return redirect()->back();
+    }
+
+    public function deleteIcon(): RedirectResponse
+    {
+        helper('filesystem');
+        // delete site folder in media
+        delete_files(ROOTPATH . 'public/media/site/');
+
+        service('settings')
+            ->forget('App.siteIcon');
+
+        return redirect()->back();
+    }
+}
diff --git a/modules/Admin/Language/en/AdminNavigation.php b/modules/Admin/Language/en/AdminNavigation.php
index 68cbefb3eb..c2e8e4b696 100644
--- a/modules/Admin/Language/en/AdminNavigation.php
+++ b/modules/Admin/Language/en/AdminNavigation.php
@@ -29,6 +29,8 @@ return [
     'pages' => 'Pages',
     'page-list' => 'All pages',
     'page-create' => 'New Page',
+    'settings' => 'Settings',
+    'settings-general' => 'General',
     'account' => [
         'my-account' => 'My account',
         'change-password' => 'Change password',
diff --git a/modules/Admin/Language/en/Breadcrumb.php b/modules/Admin/Language/en/Breadcrumb.php
index 9a1ef1c2f9..63564f79d9 100644
--- a/modules/Admin/Language/en/Breadcrumb.php
+++ b/modules/Admin/Language/en/Breadcrumb.php
@@ -16,6 +16,7 @@ return [
     'episodes' => 'episodes',
     'contributors' => 'contributors',
     'pages' => 'pages',
+    'settings' => 'settings',
     'add' => 'add',
     'new' => 'new',
     'edit' => 'edit',
diff --git a/modules/Admin/Language/en/Settings.php b/modules/Admin/Language/en/Settings.php
new file mode 100644
index 0000000000..8864c87fdb
--- /dev/null
+++ b/modules/Admin/Language/en/Settings.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright  2020 Podlibre
+ * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
+ * @link       https://castopod.org/
+ */
+
+return [
+    'title' => 'General settings',
+    'form' => [
+        'site_section_title' => 'Instance',
+        'site_icon' => 'Site icon',
+        'site_icon_delete' => 'Delete site icon',
+        'site_icon_hint' => 'Site icons are what you see on your browser tabs, bookmarks bar, and when you add a website as a shortcut on mobile devices.',
+        'site_icon_helper' => 'Icon must be squared with at least 512px wide and tall.',
+        'site_name' => 'Site name',
+        'site_description' => 'Site description',
+        'submit' => 'Save',
+    ],
+];
diff --git a/modules/Admin/Language/fr/AdminNavigation.php b/modules/Admin/Language/fr/AdminNavigation.php
index 357b64ffab..b85b1a3025 100644
--- a/modules/Admin/Language/fr/AdminNavigation.php
+++ b/modules/Admin/Language/fr/AdminNavigation.php
@@ -28,6 +28,8 @@ return [
     'pages' => 'Pages',
     'page-list' => 'Toutes les pages',
     'page-create' => 'Créer une page',
+    'settings' => 'Paramètres',
+    'settings-general' => 'Général',
     'account' => [
         'my-account' => 'Mon compte',
         'change-password' => 'Modifier le mot de passe',
diff --git a/modules/Admin/Language/fr/Settings.php b/modules/Admin/Language/fr/Settings.php
new file mode 100644
index 0000000000..e68c87c7a2
--- /dev/null
+++ b/modules/Admin/Language/fr/Settings.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright  2020 Podlibre
+ * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
+ * @link       https://castopod.org/
+ */
+
+return [
+    'title' => 'Paramètres généraux',
+    'form' => [
+        'site_section_title' => 'Instance',
+        'site_section_subtitle' => 'configuration de l’instance',
+        'site_icon' => 'Favicon du site',
+        'site_icon_delete' => 'Supprimer la favicon du site',
+        'site_icon_hint' => 'Les favicons sont ce que vous voyez sur les onglets de votre navigateur, dans votre barre de favoris, et lorsque vous ajoutez un site web en raccourci sur des appareils mobiles.',
+        'site_icon_helper' => 'La favicon doit être carrée et avoir au moins 512px de largeur et de hauteur.',
+        'site_name' => 'Titre du site',
+        'site_description' => 'Description du site',
+        'submit' => 'Save',
+    ],
+];
diff --git a/modules/Install/Controllers/InstallController.php b/modules/Install/Controllers/InstallController.php
index 59bad8c901..5a65ce4475 100644
--- a/modules/Install/Controllers/InstallController.php
+++ b/modules/Install/Controllers/InstallController.php
@@ -243,6 +243,8 @@ class InstallController extends Controller
     {
         $migrations = Services::migrations();
 
+        $migrations->setNamespace('Sparks\Settings')
+            ->latest();
         $migrations->setNamespace('Myth\Auth')
             ->latest();
         $migrations->setNamespace('Modules\Fediverse')
diff --git a/public/favicon.ico b/public/favicon.ico
index b844d1d06dc8c533874fdb40bdb0701700dda75f..9bf9b97aa31acf5e8dc1c93087eaa79b80d60396 100644
GIT binary patch
literal 21238
zcmeHPTWl0n7@mUSB_<Nl7$4Lc4MH?QV`Abp6=RHv4?byPV!Xwem?)1(LI_H13nH5B
zZfQ$t+1jpcrFU%U6<QYB(y~Rn?Y6boEfhgChywcbd%kIAoEdh`>=nB4Z*p>G&gH+&
zcP_K@ohnL(vP{X$R79SsRIXH%)rz8IWi8&XS+6JoQMN{ui6)9DR@;dz;$OB~Ax<0v
z#sFi0F~ArA21;t5rJfNF{dl^Sh8H>$N|4bLbkp9Nr=wVBGN@xHhvxrYpi}>xNos=3
z_7k5=ZD{EED5*Ll`A~QGgNQclD7clT|D4eEkDVW&{>cgoUk=mkrD^I8=F!CO0lhww
zY?O_h_0!SN9-6y6C(<k(8!w^B3qif#)Wr~O-+l8{HtefkueX7m=Dv+;n;g$_%JD2y
zuiFN<n%YFNp~=6&&~LeV-F0$@sb6W`b85M9zNv11)6_=OZ*0>x_?fAVOuw<8Rex3M
z>sRS7Kk$l4_SpG;`m*pgz1_||D@FW=Ol>0RNBp-J+@kl}ReYx@{^(QI^s-*B+4)uR
z9qO|-$_$=4MEg76rkbvI)#qxglU@I`&`$e1-lEz=?^Ed4UX#th6Le{Mjd@2P+tgO3
zBi_2sq1=eNVF%)NVzxGAHU#Dx3FFM@v-LVR|3<<*FnZ1}=AqLG+k$y#^xQFO^nXIR
z`75Nlt+rt7!Jb4CWLV|$J+JeO0mc9@5b;NWYgmZiDY8V`DiVIru=``YGF}-2i~+^~
zV}LQh7+?%A1{ed30mcAhfHA-rU<@z@7z2y}#sFi0F~At`l&==wzu%*dz!!o$;SwB_
zyz81^18k}3dWYQKKA5s^vZZy;)6o2Zguh$I`+Yx}e)uLHm}?N6pr?`+2Ye$3&m$Gw
zp=f-Hr_CZKX`A5J+>lTl;5)cN@z}5w`WziA7BPsQrvOgLQt%@Y8(Igp#D#&9+Go-V
z^L8-;*QTiYiI`&$2shb2dnf+R^u;j>UpZyl-_BOM@|nMWRylWe^{_`aKMhP*$K-#$
zZ>`k!mi|pt{QZ;EJ?zpsXwVNnQcK?^!2?@G$s9ZN-5-+6d%xgtW}CQj<G&56YoDj$
zVZvCqkIw?-l|2x348W<flZA4Z_db)Xokh0`{?~upJnRz-E7wV7$mARAfFp1OjeAe<
z2aOy;)Oj1sJWHbtT-)(OjdHbXf2hdTc5q3xcCs96ht-C7?n8nvDbu6XeO+?5R3EUa
zwROs6V}B-U|5r%ui8dC=@;{b2;N*O>)OMHmUdLD#Ry`(_5v~6!{>AumdS+Ihu~k0!
zpH4Eg`u<6u)b^IXPaS1hxxrR@V!6lsr`cwg%jQ4m>K!k2l%dIX;zNV~T~zhBt-O8p
zFNpOhdFvnM9K3gF_E~dZ^J~)e5B?uKzS*|FRbE-+OVZ~Ld9bOf?G4lZ5_2Bj4QDUS
zIQEa_`J*kk_7y+8DtyS${Q%#-@yN$wuMu|C=WTdV?`QSbdJjJ)FAO`%T(oOgyd(JN
z_*5m~3_z@Y#o8WRdAyTgJ~N(ckjX*pV&zC9t}>bFREPbH^%*<eBVNH@4ySz#M$R4)
z=TdHp*?+{xUH%YF{V|%hF~Io9uUH$G?;1V|zI)WP=S~;gW6a0%-lzF(%y;d*i?An_
zy<;t<xb#kV>6_Af5_;}N|F!GgzDLAaw{2ox=#Fa5xmGa=Uzr!@n0mxJXAWWCso9&_
zJVQP6gL6OEJ7v2U&(WpUU;gc%<I5Oe3@`>51B?O20AqkLz!+c*Fa{U{i~+_#dSbxt
M{{nFN|04qb0s-QaO8@`>

literal 16958
zcmeHPOH30{6dhvx#l%mHM2!p77)dlPOx)-~;=;tl7^4eyp&Mgjj0uUlaH;WwHJ}hE
zl#i4k(DD(Ge}E#CUqA|#1_;Wx)Hbrvg(2wb`fkaPu}r<w7DEfS<T2AXGv}Uj@0s^r
zJ4q!eO8mveN#cKu6njRJq9sX+6-{(Wk3_?M?@Jupxn-d(bV$<kGzqwI4%h?i0rmiU
zfIYw-U=Oed*aPeV_5gc;J-{Ad53mQ=1MC6z0DFKvu(myrX+8$k9r4gUoDMx>MPQpM
zU(wb(Zidd0EU4|e4>_eL#Iua3|MEP;SaBK#-A&;7?Fs8P^vwzdHRqNs2MPLh(A@tt
ztTn6Eq<u&aFAbYR%tI6)jv9U>^z}`a!i)6vLFb^R>t4iam+RAF*9E0Nz3?#1{BlLS
ze#}GeyOROupkpL+RcJ^3{nM2J=}*eu4m0yJt42TOAnolz%{eHlyFh9mp6vj<x2ERC
zmz?YHSfIKyeo4Q4PX@ZR3?#spA9fWyH*|l_gVFCcm1BR;(-p0^H0!VLc}U_FH(v4L
z=`uD#Zuu!FX}k)ihD##P2NvV>j+;mvDi;IJYA+tn!x*R1dfS^@vxxONH`;rw%yNT_
z)8<HC<VCIF+Z-t*o>Hejyz&n>NgPUlZTAC`cS_zqG7i?lA5Y(YE&5aQ_mg<$hRY-`
zN<Y@ym!I!@oZ~5+j}yftj*Q!6dqUzB)n6p>mSf%6I+#Sp$uJ!u`C7#~PS!_l%PiMP
zJiDtN@SgOC_3F(onD{wL;!t%%jHH|$e&b+#9ET~Ks`k78#8B!;&5t4RFdo)MEeAM%
z4mhhcVNksBIDXF5Wo%I8Us!uy#Br0c45p*1*DG}^_i^0S!p-4shLWahU=SY6Dmf1C
zt#`y+^-0sUTJYs_21T6ws<WUoZ1U>R7aS6w%btlf>Q^!r&c{k^TJ|xP96Q{$&Ut4*
z+|VOhH2a>@J!%XnPPpX>`Sgqx1f?Bwk(j*=aGe*9y0mZ_p6w*ZPT!jQzE3vp7T^0c
zM_djZo)$<p?g_f~$~@qh)7bYI0=@6F)M&ZSL5|2(9w(JLLfua>?gd<r+Ff-pJvSLP
zo<J_h3ArK1P<3j(9@m|TSzA}Mh4THDR-Js_+J5H2_2>Gt2iODb0rmiUfIYw-U=Oed
V*aPeV_CTb2K>i&7-mc9I`~mRN%klsK

diff --git a/public/icon-180.png b/public/icon-180.png
new file mode 100644
index 0000000000000000000000000000000000000000..937c4c6c80ad0926a8fbfc1783d26c76e0698072
GIT binary patch
literal 17522
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

literal 0
HcmV?d00001

diff --git a/public/icon-192.png b/public/icon-192.png
new file mode 100644
index 0000000000000000000000000000000000000000..7864f7e9495a68e5e5bc6958d602bab2c4733a2c
GIT binary patch
literal 5913
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
LT(?2nCFXwsK6V1%

literal 0
HcmV?d00001

diff --git a/public/icon-512.png b/public/icon-512.png
new file mode 100644
index 0000000000000000000000000000000000000000..9998e36ae442c3aa2a55834aa093163d5dd05dbd
GIT binary patch
literal 7340
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

literal 0
HcmV?d00001

diff --git a/public/icon-64.png b/public/icon-64.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf8d2cf1cc1cb9704de282006a76a1ce86568be7
GIT binary patch
literal 738
zcmV<80v-K{P)<h;3K|Lk000e1NJLTq002M$002M;0{{R3owtGP0003yP)t-s0F;IW
zmWU3Tj2fSjbjP)}<j~*s;ph3}2$+e1%(~+D;uxQhyynur=hGRVkPn-T#Ol_?>edmQ
zji1@aq}<6yvZ742rL*GCyynwcyQ(Xvm@=xGYQnM6@7pD$m4(i{<@n>h=hGmfla12A
z1C@smn~k#K&!*hT2A7HE`Q$98m|eZBx8%?josQY_-igq@Y{RmH&AU*yri9MCE2o#0
z)xwd}!6BlPQ@E&T!mz2{%OazcW52E(ppmED%6Q4QSGuVOmx(*Bo>;o7Qn;sY#j{el
zs1}}&UcIc4)4@)*rrq`6htIvx@7ti-$Ib59Wx%iF_v0g@l$qDWu;I=prIzIQ<2tRL
za>ld)l!len!>ixS37CpMub;c-(!%N0NwlOcsF`oXvzFGwuHen6-O8HR#dF5BHLIMi
zs+|e|000SaNLh0L01m_e01m_fl`9S#0003^Nkl<ZScUDBS5v}36h=1^*np^50J|bV
z#6s+f1r^2KJJ`Gb{};&0T;^sslI(+Tdmh5%Y|i(P$v}ugp-}u2T7wH(o!F=Q1Jsp(
zU-voYMlj@FR|y3Tv2P4}%^6M2eBTn0=_ze(;|AL0dP+O&ebifp-%`vr5U=^puI`@R
zKBqbyHAw%!AlxQ!KU8}vX|ns&u-K<ZejklxK#Zb+F_7bCRZL|0J_~Qn0yBvQra(@E
znc)YLa54*K4h_tMTmZAk59HuP1GD5buncB}AJE}s6-*uttbtr78->lS%1oXgh!eAr
zD{k+QT`+rSU>{_}I$%GEBHvSbc*L%m;}a04Xdrba2Ckgr58*<-zr;Pcx|SZeu?>)0
z+1|4Eq*Rvc?}WcLQ~37kQNHK<3J;H7^QUKrobmGdmVS5P<MZpg^hd8yC=?3C56@6N
U{Wi+Bp8x;=07*qoM6N<$g1oAOJ^%m!

literal 0
HcmV?d00001

diff --git a/public/icon.png b/public/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c182ad441f3c993ef0d680db2293ebe0018bcb3
GIT binary patch
literal 7027
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
W;iiJ7JFFZTxO&O>VyPbH{(k`UFo%%<

literal 0
HcmV?d00001

diff --git a/themes/cp_admin/_layout.php b/themes/cp_admin/_layout.php
index 3b673615ab..17274d51df 100644
--- a/themes/cp_admin/_layout.php
+++ b/themes/cp_admin/_layout.php
@@ -7,7 +7,10 @@
     <title><?= $this->renderSection('title') ?> | Castopod Admin</title>
     <meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
 
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
diff --git a/themes/cp_admin/_sidebar.php b/themes/cp_admin/_sidebar.php
index 0a1a7f410a..dd8cb12a4d 100644
--- a/themes/cp_admin/_sidebar.php
+++ b/themes/cp_admin/_sidebar.php
@@ -22,6 +22,10 @@ $navigation = [
         'items' => ['page-list', 'page-create'],
 
     ],
+    'settings' => [
+        'icon' => 'settings',
+        'items' => ['settings-general'],
+    ],
 ]; ?>
 
 <nav class="flex flex-col flex-1 py-4 overflow-y-auto gap-y-4">
diff --git a/themes/cp_admin/person/edit.php b/themes/cp_admin/person/edit.php
index 49c2ca484d..7ce8966e79 100644
--- a/themes/cp_admin/person/edit.php
+++ b/themes/cp_admin/person/edit.php
@@ -35,6 +35,7 @@
     label="<?= lang('Person.form.unique_name') ?>"
     hint="<?= lang('Person.form.unique_name_hint') ?>"
     required="true" />
+
 <Forms.Field
     name="information_url"
     label="<?= lang('Person.form.information_url') ?>"
diff --git a/themes/cp_admin/settings/general.php b/themes/cp_admin/settings/general.php
new file mode 100644
index 0000000000..f48bc58136
--- /dev/null
+++ b/themes/cp_admin/settings/general.php
@@ -0,0 +1,59 @@
+<?= $this->extend('_layout') ?>
+
+<?= $this->section('title') ?>
+<?= lang('Settings.title') ?>
+<?= $this->endSection() ?>
+
+<?= $this->section('pageTitle') ?>
+<?= lang('Settings.title') ?>
+<?= $this->endSection() ?>
+
+<?= $this->section('content') ?>
+
+<form action="<?= route_to('settings-instance') ?>" method="POST" class="flex flex-col max-w-sm gap-y-4" enctype="multipart/form-data">
+<?= csrf_field() ?>
+
+<Forms.Section
+    title="<?= lang('Settings.form.site_section_title') ?>">
+
+    <Forms.Field
+        name="site_name"
+        label="<?= lang('Settings.form.site_name') ?>"
+        value="<?= service('settings')
+    ->get('App.siteName') ?>"
+        required="true" />
+
+    <Forms.Field
+        as="Textarea"
+        name="site_description"
+        label="<?= lang('Settings.form.site_description') ?>"
+        value="<?= service('settings')
+    ->get('App.siteDescription') ?>"
+        required="true"
+        rows="4" />
+
+    <div class="flex items-center">
+        <Forms.Field
+            name="site_icon"
+            type="file"
+            label="<?= lang('Settings.form.site_icon') ?>"
+            hint="<?= lang('Settings.form.site_icon_hint') ?>"
+            helper="<?= lang('Settings.form.site_icon_helper') ?>"
+            accept=".png,.jpeg"
+            class="flex-1"
+            />
+        <?php if (config('App')->siteIcon['ico'] !== service('settings')->get('App.siteIcon')['ico']): ?>
+        <div class="relative ml-2">
+            <a href="<?= route_to('settings-instance-delete-icon') ?>" class="absolute p-1 text-white bg-red-600 border-2 border-black rounded-full hover:bg-red-800 -top-3 -right-3 focus:ring-castopod" title="<?= lang('Settings.form.site_icon_delete') ?>"><?= icon('delete-bin') ?></a>
+            <img src="<?= service('settings')->get('App.siteIcon')['64'] ?>" alt="<?= service('settings')->get('App.siteName') ?> Favicon" class="w-10 h-10" />
+        </div>
+        <?php endif; ?>
+    </div>
+
+    <Button variant="primary" type="submit" class="self-end"><?= lang('Settings.form.submit') ?></Button>
+
+</Forms.Section>
+
+</form>
+
+<?= $this->endSection() ?>
diff --git a/themes/cp_app/_layout.php b/themes/cp_app/_layout.php
index eda77ca644..54afb9f285 100644
--- a/themes/cp_app/_layout.php
+++ b/themes/cp_app/_layout.php
@@ -6,9 +6,20 @@
 <head>
     <meta charset="UTF-8"/>
     <title><?= $this->renderSection('title') ?></title>
-    <meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
+    <meta name="description" content="<?= service('settings')
+    ->get('App.siteDescription') ?>"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
+
+    <meta property="og:title" content="<?= service('settings')
+    ->get('App.siteName') ?>" />
+    <meta property="og:description" content="<?= service('settings')
+    ->get('App.siteDescription') ?>" />
+    <meta property="og:site_name" content="<?= service('settings')
+    ->get('App.siteName') ?>" />
 
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
diff --git a/themes/cp_app/embed.php b/themes/cp_app/embed.php
index 2817de36ac..612a5f1f03 100644
--- a/themes/cp_app/embed.php
+++ b/themes/cp_app/embed.php
@@ -8,7 +8,10 @@
     <meta name="description" content="<?= htmlspecialchars(
         $episode->description,
     ) ?>" />
-    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
     <link rel="canonical" href="<?= $episode->link ?>" />
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
diff --git a/themes/cp_app/episode/_layout.php b/themes/cp_app/episode/_layout.php
index 8eb6efd872..813c198387 100644
--- a/themes/cp_app/episode/_layout.php
+++ b/themes/cp_app/episode/_layout.php
@@ -7,7 +7,10 @@
 <head>
     <meta charset="UTF-8"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
 
     <?= $this->renderSection('meta-tags') ?>
     <?php if ($podcast->payment_pointer): ?>
diff --git a/themes/cp_app/home.php b/themes/cp_app/home.php
index 310f50539e..0852e85c05 100644
--- a/themes/cp_app/home.php
+++ b/themes/cp_app/home.php
@@ -5,10 +5,23 @@
 
 <head>
     <meta charset="UTF-8"/>
-    <title>Castopod</title>
-    <meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
+    <title><?= service('settings')
+    ->get('App.siteName') ?></title>
+    <meta name="description" content="<?= service('settings')
+    ->get('App.siteDescription') ?>"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
+
+    <meta property="og:title" content="<?= service('settings')
+    ->get('App.siteName') ?>" />
+    <meta property="og:description" content="<?= service('settings')
+    ->get('App.siteDescription') ?>" />
+    <meta property="og:site_name" content="<?= service('settings')
+    ->get('App.siteName') ?>" />
+
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
     <?= service('vite')
diff --git a/themes/cp_app/map.php b/themes/cp_app/map.php
index e59fcd0168..2838dcea8d 100644
--- a/themes/cp_app/map.php
+++ b/themes/cp_app/map.php
@@ -8,7 +8,10 @@
     <title><?= lang('Page.map') ?></title>
     <meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
     <?= service('vite')
diff --git a/themes/cp_app/page.php b/themes/cp_app/page.php
index b8c0f8e7f4..84399887f1 100644
--- a/themes/cp_app/page.php
+++ b/themes/cp_app/page.php
@@ -7,7 +7,10 @@
     <meta charset="UTF-8"/>
     <title><?= $page->title ?></title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
     <?= service('vite')
diff --git a/themes/cp_app/podcast/_layout.php b/themes/cp_app/podcast/_layout.php
index 487a58abcf..8a9c9eea16 100644
--- a/themes/cp_app/podcast/_layout.php
+++ b/themes/cp_app/podcast/_layout.php
@@ -7,7 +7,10 @@
 <head>
     <meta charset="UTF-8"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
 
     <?= $this->renderSection('meta-tags') ?>
     <?php if ($podcast->payment_pointer): ?>
diff --git a/themes/cp_app/podcast/about.php b/themes/cp_app/podcast/about.php
index 7383c93cd0..1c80116ca4 100644
--- a/themes/cp_app/podcast/about.php
+++ b/themes/cp_app/podcast/about.php
@@ -9,7 +9,10 @@
 <meta name="description" content="<?= htmlspecialchars(
     $podcast->description,
 ) ?>" />
-<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+<link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+<link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+<link rel="manifest" href="<?= route_to('webmanifest') ?>">
 <link rel="canonical" href="<?= current_url() ?>" />
 <meta property="og:title" content="<?= $podcast->title ?>" />
 <meta property="og:description" content="<?= $podcast->description ?>" />
diff --git a/themes/cp_app/podcast/activity.php b/themes/cp_app/podcast/activity.php
index f291f25794..3743d8859b 100644
--- a/themes/cp_app/podcast/activity.php
+++ b/themes/cp_app/podcast/activity.php
@@ -7,7 +7,10 @@
 <meta name="description" content="<?= htmlspecialchars(
     $podcast->description,
 ) ?>" />
-<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+<link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+<link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+<link rel="manifest" href="<?= route_to('webmanifest') ?>">
 <link rel="canonical" href="<?= current_url() ?>" />
 <meta property="og:title" content="<?= $podcast->title ?>" />
 <meta property="og:description" content="<?= $podcast->description ?>" />
diff --git a/themes/cp_app/podcast/episodes.php b/themes/cp_app/podcast/episodes.php
index c5a97c9aee..8b6bae0fe4 100644
--- a/themes/cp_app/podcast/episodes.php
+++ b/themes/cp_app/podcast/episodes.php
@@ -7,7 +7,10 @@
 <meta name="description" content="<?= htmlspecialchars(
     $podcast->description,
 ) ?>" />
-<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+<link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+<link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+<link rel="manifest" href="<?= route_to('webmanifest') ?>">
 <link rel="canonical" href="<?= current_url() ?>" />
 <meta property="og:title" content="<?= $podcast->title ?>" />
 <meta property="og:description" content="<?= $podcast->description ?>" />
diff --git a/themes/cp_app/podcast/follow.php b/themes/cp_app/podcast/follow.php
index 473980cf75..abd8888d77 100644
--- a/themes/cp_app/podcast/follow.php
+++ b/themes/cp_app/podcast/follow.php
@@ -7,8 +7,10 @@
 <head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
-    <?= service('vite')->asset('styles/index.css', 'css') ?>
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
     
     <title><?= lang('Podcast.followTitle', [
         'actorDisplayName' => $actor->display_name,
diff --git a/themes/cp_app/post/remote_action.php b/themes/cp_app/post/remote_action.php
index 3a885e9ee1..6914f04bee 100644
--- a/themes/cp_app/post/remote_action.php
+++ b/themes/cp_app/post/remote_action.php
@@ -5,7 +5,10 @@
 <head>
     <meta charset="UTF-8"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
 
     <title><?= lang('Fediverse.' . $action . '.title', [
         'actorDisplayName' => $post->actor->display_name,
diff --git a/themes/cp_auth/_layout.php b/themes/cp_auth/_layout.php
index b22e49856d..9ac33873f9 100644
--- a/themes/cp_auth/_layout.php
+++ b/themes/cp_auth/_layout.php
@@ -7,7 +7,7 @@
 	<title>Castopod Auth</title>
 	<meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
 	<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-	<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+	<link rel="icon" type="image/x-icon" href="/favicon.ico" />
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
 </head>
diff --git a/themes/cp_install/_layout.php b/themes/cp_install/_layout.php
index a3ed84c476..affa0827c6 100644
--- a/themes/cp_install/_layout.php
+++ b/themes/cp_install/_layout.php
@@ -6,7 +6,10 @@
     <title>Castopod</title>
     <meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
-    <link rel="shortcut icon" type="image/png" href="/favicon.ico" />
+    <link rel="icon" type="image/x-icon" href="<?= service('settings')
+    ->get('App.siteIcon')['ico'] ?>" />
+    <link rel="apple-touch-icon" href="<?= service('settings')->get('App.siteIcon')['180'] ?>">
+    <link rel="manifest" href="<?= route_to('webmanifest') ?>">
     <?= service('vite')
         ->asset('styles/index.css', 'css') ?>
     <?= service('vite')
-- 
GitLab