feat(settings): add general config for instance (site name, description and icon)

This commit is contained in:
Yassine Doghri 2021-10-26 15:54:56 +00:00
parent 193b373bc9
commit 5c56f3e6f0
41 changed files with 533 additions and 52 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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',
];
}

View File

@ -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',
]);

View File

@ -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);
}
}

View File

@ -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',

View File

@ -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');
}

View File

@ -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",

153
composer.lock generated
View File

@ -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"
}

View File

@ -17,7 +17,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/themes',
__DIR__ . '/tests',
__DIR__ . '/public',
__DIR__ . '/public',
]);
$parameters->set(Option::SKIP, [

View File

@ -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',

View File

@ -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();
}
}

View File

@ -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',

View File

@ -16,6 +16,7 @@ return [
'episodes' => 'episodes',
'contributors' => 'contributors',
'pages' => 'pages',
'settings' => 'settings',
'add' => 'add',
'new' => 'new',
'edit' => 'edit',

View File

@ -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',
],
];

View File

@ -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',

View File

@ -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 linstance',
'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',
],
];

View File

@ -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')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
public/icon-180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
public/icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
public/icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
public/icon-64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -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') ?>

View File

@ -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">

View File

@ -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') ?>"

View File

@ -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() ?>

View File

@ -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') ?>

View File

@ -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') ?>

View File

@ -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): ?>

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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): ?>

View File

@ -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 ?>" />

View File

@ -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 ?>" />

View File

@ -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 ?>" />

View File

@ -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,

View File

@ -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,

View File

@ -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>

View File

@ -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')