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