diff --git a/app/Config/App.php b/app/Config/App.php index 2c5e100ff852ac25ea50eb98cd25f5267fbce424..d547394793470dd190374e76341497f0d1996b2a 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -435,6 +435,8 @@ class App extends BaseConfig */ public string $siteName = 'Castopod'; + public string $siteTitleSeparator = ' | '; + public string $siteDescription = 'Castopod Host is an open-source hosting platform made for podcasters who want engage and interact with their audience.'; /** diff --git a/app/Config/Embed.php b/app/Config/Embed.php new file mode 100644 index 0000000000000000000000000000000000000000..0c97f86f08b57e7a2286662641a7c30ea77dff7d --- /dev/null +++ b/app/Config/Embed.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace Config; + +use CodeIgniter\Config\BaseConfig; + +class Embed extends BaseConfig +{ + /** + * -------------------------------------------------------------------------- + * Embeddable player config + * -------------------------------------------------------------------------- + */ + public int $width = 600; + + public int $height = 144; +} diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 7d2ca19f731c82e43055b9850624a0a6a5ff9344..161c02fa4abc20a73312e7e1f92e48b337c46976 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -180,10 +180,10 @@ $routes->group('@(:podcastHandle)', function ($routes): void { $routes->get('/credits', 'CreditsController', [ 'as' => 'credits', ]); -$routes->get('/map', 'MapMarkerController', [ +$routes->get('/map', 'MapController', [ 'as' => 'map', ]); -$routes->get('/episodes-markers', 'MapMarkerController::getEpisodesMarkers', [ +$routes->get('/episodes-markers', 'MapController::getEpisodesMarkers', [ 'as' => 'episodes-markers', ]); $routes->get('/pages/(:slug)', 'PageController/$1', [ diff --git a/app/Controllers/ActorController.php b/app/Controllers/ActorController.php index ff1dbae54b5fad448d46f939d60c77481dad3dbd..b7233df4decfa05880c0879454455c73d17bf507 100644 --- a/app/Controllers/ActorController.php +++ b/app/Controllers/ActorController.php @@ -20,7 +20,7 @@ class ActorController extends FediverseActorController /** * @var string[] */ - protected $helpers = ['auth', 'svg', 'components', 'misc']; + protected $helpers = ['auth', 'svg', 'components', 'misc', 'seo']; public function follow(): string { @@ -34,6 +34,8 @@ class ActorController extends FediverseActorController if (! ($cachedView = cache($cacheName))) { helper(['form', 'components', 'svg']); $data = [ + // @phpstan-ignore-next-line + 'metatags' => get_follow_metatags($this->actor), 'actor' => $this->actor, ]; diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 2d30eb3c5602994967bd63f42de26d75cd23fb63..312f1ec2fbe3af78622b9673faff7d5f5dbe7025 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -28,7 +28,7 @@ class BaseController extends Controller ResponseInterface $response, LoggerInterface $logger ): void { - $this->helpers = array_merge($this->helpers, ['auth', 'svg', 'components', 'misc']); + $this->helpers = array_merge($this->helpers, ['auth', 'svg', 'components', 'misc', 'seo']); // Do Not Edit This Line parent::initController($request, $response, $logger); diff --git a/app/Controllers/CreditsController.php b/app/Controllers/CreditsController.php index 816653dd15006ec31fa9e9823a5a634add92a0c1..3531b3e2e86e8c230426a03ae1895564c4f5ee8a 100644 --- a/app/Controllers/CreditsController.php +++ b/app/Controllers/CreditsController.php @@ -165,11 +165,12 @@ class CreditsController extends BaseController } $data = [ + 'metatags' => get_page_metatags($page), 'page' => $page, 'credits' => $credits, ]; - $found = view('credits', $data); + $found = view('pages/credits', $data); cache() ->save($cacheName, $found, DECADE); diff --git a/app/Controllers/EpisodeCommentController.php b/app/Controllers/EpisodeCommentController.php index f64c1db30428f4514c9f7ce0e827133df6188577..6bb1ef0e67909595d7e9eb52fc7d2ab743e40fa2 100644 --- a/app/Controllers/EpisodeCommentController.php +++ b/app/Controllers/EpisodeCommentController.php @@ -95,6 +95,7 @@ class EpisodeCommentController extends BaseController if (! ($cachedView = cache($cacheName))) { $data = [ + 'metatags' => get_episode_comment_metatags($this->comment), 'podcast' => $this->podcast, 'actor' => $this->actor, 'episode' => $this->episode, diff --git a/app/Controllers/EpisodeController.php b/app/Controllers/EpisodeController.php index 3a59f91a2788785875176dd89d608147091304c8..e79b9be5f0f8ab4a1c25a803de5495add274efc0 100644 --- a/app/Controllers/EpisodeController.php +++ b/app/Controllers/EpisodeController.php @@ -77,6 +77,7 @@ class EpisodeController extends BaseController if (! ($cachedView = cache($cacheName))) { $data = [ + 'metatags' => get_episode_metatags($this->episode), 'podcast' => $this->podcast, 'episode' => $this->episode, ]; @@ -115,6 +116,7 @@ class EpisodeController extends BaseController if (! ($cachedView = cache($cacheName))) { $data = [ + 'metatags' => get_episode_metatags($this->episode), 'podcast' => $this->podcast, 'episode' => $this->episode, ]; @@ -220,20 +222,21 @@ class EpisodeController extends BaseController $oembed->addChild('author_name', $this->podcast->title); $oembed->addChild('author_url', $this->podcast->link); $oembed->addChild('thumbnail', $this->episode->cover->large_url); - $oembed->addChild('thumbnail_width', config('Images')->podcastCoverSizes['large'][0]); - $oembed->addChild('thumbnail_height', config('Images')->podcastCoverSizes['large'][1]); + $oembed->addChild('thumbnail_width', (string) config('Images')->podcastCoverSizes['large'][0]); + $oembed->addChild('thumbnail_height', (string) config('Images')->podcastCoverSizes['large'][1]); $oembed->addChild( 'html', htmlentities( '<iframe src="' . $this->episode->embed_url . - '" width="100%" height="144" frameborder="0" scrolling="no"></iframe>', + '" width="100%" height="' . config('Embed')->height . '" frameborder="0" scrolling="no"></iframe>', ), ); - $oembed->addChild('width', '600'); - $oembed->addChild('height', '144'); + $oembed->addChild('width', (string) config('Embed')->width); + $oembed->addChild('height', (string) config('Embed')->height); - return $this->response->setXML((string) $oembed); + // @phpstan-ignore-next-line + return $this->response->setXML($oembed); } /** diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php index f19eb74d1e976bd1d11b7822549184bad7e15bb2..aeaaff0ab76745ae8640b2ba8a907a7e80a5a0e2 100644 --- a/app/Controllers/HomeController.php +++ b/app/Controllers/HomeController.php @@ -36,6 +36,7 @@ class HomeController extends BaseController // default behavior: list all podcasts on home page $data = [ + 'metatags' => get_home_metatags(), 'podcasts' => $allPodcasts, ]; diff --git a/app/Controllers/MapMarkerController.php b/app/Controllers/MapController.php similarity index 95% rename from app/Controllers/MapMarkerController.php rename to app/Controllers/MapController.php index 5ead95979baab6bca5533631523a1641ebcbaccb..c4a6cd4bde29f23f39d11e9d332a8a058c395c31 100644 --- a/app/Controllers/MapMarkerController.php +++ b/app/Controllers/MapController.php @@ -13,7 +13,7 @@ namespace App\Controllers; use App\Models\EpisodeModel; use CodeIgniter\HTTP\ResponseInterface; -class MapMarkerController extends BaseController +class MapController extends BaseController { public function index(): string { @@ -21,7 +21,7 @@ class MapMarkerController extends BaseController ->getLocale(); $cacheName = "page_map_{$locale}"; if (! ($found = cache($cacheName))) { - $found = view('map', [], [ + $found = view('pages/map', [], [ 'cache' => DECADE, 'cache_name' => $cacheName, ]); diff --git a/app/Controllers/PageController.php b/app/Controllers/PageController.php index f1e1bf2a79966462c795f2d712bb7a3ae63b168b..3a251a7dca7e5fc00078fd83a34d7ab1ccf5f730 100644 --- a/app/Controllers/PageController.php +++ b/app/Controllers/PageController.php @@ -40,10 +40,11 @@ class PageController extends BaseController $cacheName = "page-{$this->page->slug}"; if (! ($found = cache($cacheName))) { $data = [ + 'metatags' => get_page_metatags($this->page), 'page' => $this->page, ]; - $found = view('page', $data); + $found = view('pages/page', $data); // The page cache is set to a decade so it is deleted manually upon page update cache() diff --git a/app/Controllers/PodcastController.php b/app/Controllers/PodcastController.php index f330064d436f8e4861d1a422e5dbe21f95e6f84f..6d0b6c690e4349db0dde5f524e7b3cbb517c0da1 100644 --- a/app/Controllers/PodcastController.php +++ b/app/Controllers/PodcastController.php @@ -80,6 +80,7 @@ class PodcastController extends BaseController if (! ($cachedView = cache($cacheName))) { $data = [ + 'metatags' => get_podcast_metatags($this->podcast, 'activity'), 'podcast' => $this->podcast, 'posts' => (new PostModel())->getActorPublishedPosts($this->podcast->actor_id), ]; @@ -125,6 +126,7 @@ class PodcastController extends BaseController if (! ($cachedView = cache($cacheName))) { $data = [ + 'metatags' => get_podcast_metatags($this->podcast, 'about'), 'podcast' => $this->podcast, ]; @@ -240,6 +242,7 @@ class PodcastController extends BaseController } $data = [ + 'metatags' => get_podcast_metatags($this->podcast, 'episodes'), 'podcast' => $this->podcast, 'episodesNav' => $episodesNavigation, 'activeQuery' => $activeQuery, diff --git a/app/Controllers/PostController.php b/app/Controllers/PostController.php index 69837feb0d369e143df1502f8af8e0a3eaabd4f5..0edb02dca51d6402194b448c8744e7b08148a842 100644 --- a/app/Controllers/PostController.php +++ b/app/Controllers/PostController.php @@ -35,7 +35,7 @@ class PostController extends FediversePostController /** * @var string[] */ - protected $helpers = ['auth', 'fediverse', 'svg', 'components', 'misc']; + protected $helpers = ['auth', 'fediverse', 'svg', 'components', 'misc', 'seo']; public function _remap(string $method, string ...$params): mixed { @@ -81,6 +81,8 @@ class PostController extends FediversePostController if (! ($cachedView = cache($cacheName))) { $data = [ + // @phpstan-ignore-next-line + 'metatags' => get_post_metatags($this->post), 'post' => $this->post, 'podcast' => $this->podcast, ]; @@ -233,6 +235,8 @@ class PostController extends FediversePostController if (! ($cachedView = cache($cacheName))) { $data = [ + // @phpstan-ignore-next-line + 'metatags' => get_remote_actions_metatags($this->post, $action), 'podcast' => $this->podcast, 'actor' => $this->actor, 'post' => $this->post, diff --git a/app/Helpers/page_helper.php b/app/Helpers/page_helper.php index beb1eb27b7a6914081cf78febd60151d31446785..7817a4d9398cc72fa140438c735625c22d0b8c48 100644 --- a/app/Helpers/page_helper.php +++ b/app/Helpers/page_helper.php @@ -25,7 +25,7 @@ if (! function_exists('render_page_links')) { $links .= anchor(route_to('credits'), lang('Person.credits'), [ 'class' => 'px-2 py-1 underline hover:no-underline focus:ring-accent', ]); - $links .= anchor(route_to('map'), lang('Page.map'), [ + $links .= anchor(route_to('map'), lang('Page.map.title'), [ 'class' => 'px-2 py-1 underline hover:no-underline focus:ring-accent', ]); foreach ($pages as $page) { diff --git a/app/Helpers/seo_helper.php b/app/Helpers/seo_helper.php new file mode 100644 index 0000000000000000000000000000000000000000..36edf1cb2c883f703156af5ceb999180532ef84a --- /dev/null +++ b/app/Helpers/seo_helper.php @@ -0,0 +1,275 @@ +<?php + +declare(strict_types=1); + +use App\Entities\Actor; +use App\Entities\Episode; +use App\Entities\EpisodeComment; +use App\Entities\Page; +use App\Entities\Podcast; +use App\Entities\Post; +use Melbahja\Seo\MetaTags; +use Melbahja\Seo\Schema; +use Melbahja\Seo\Schema\Thing; + +/** + * @copyright 2021 Podlibre + * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 + * @link https://castopod.org/ + */ + +if (! function_exists('get_podcast_metatags')) { + function get_podcast_metatags(Podcast $podcast, string $page): string + { + $schema = new Schema( + new Thing('PodcastSeries', [ + 'name' => $podcast->title, + 'url' => url_to('podcast-activity', $podcast->handle), + 'image' => $podcast->cover->feed_url, + 'description' => $podcast->description, + 'webFeed' => $podcast->feed_url, + 'author' => new Thing('Person', [ + 'name' => $podcast->publisher, + ]), + ]) + ); + + $metatags = new MetaTags(); + + $metatags + ->title(' ' . $podcast->title . " (@{$podcast->handle})" . ' • ' . lang('Podcast.' . $page)) + ->description(htmlspecialchars($podcast->description)) + ->image((string) $podcast->cover->large_url) + ->canonical((string) current_url()) + ->og('image:width', (string) config('Images')->podcastCoverSizes['large'][0]) + ->og('image:height', (string) config('Images')->podcastCoverSizes['large'][1]) + ->og('locale', $podcast->language_code) + ->og('site_name', service('settings')->get('App.siteName')); + + if ($podcast->payment_pointer) { + $metatags->meta('monetization', $podcast->payment_pointer); + } + + return '<link type="application/rss+xml" rel="alternate" title="' . $podcast->title . '" href="' . $podcast->feed_url . '" />' . PHP_EOL . $metatags->__toString() . PHP_EOL . $schema->__toString(); + } +} + +if (! function_exists('get_episode_metatags')) { + function get_episode_metatags(Episode $episode): string + { + $schema = new Schema( + new Thing('PodcastEpisode', [ + 'url' => url_to('episode', $episode->podcast->handle, $episode->slug), + 'name' => $episode->title, + 'image' => $episode->cover->feed_url, + 'description' => $episode->description, + 'datePublished' => $episode->published_at->format(DATE_ISO8601), + 'timeRequired' => iso8601_duration($episode->audio_file_duration), + 'associatedMedia' => new Thing('MediaObject', [ + 'contentUrl' => $episode->audio_file_url, + ]), + 'partOfSeries' => new Thing('PodcastSeries', [ + 'name' => $episode->podcast->title, + 'url' => url_to('podcast-activity', $episode->podcast->handle), + ]), + ]) + ); + + $metatags = new MetaTags(); + + $metatags + ->title($episode->title) + ->description(htmlspecialchars($episode->description)) + ->image((string) $episode->cover->large_url, 'player') + ->canonical($episode->link) + ->og('site_name', service('settings')->get('App.siteName')) + ->og('image:width', (string) config('Images')->podcastCoverSizes['large'][0]) + ->og('image:height', (string) config('Images')->podcastCoverSizes['large'][1]) + ->og('locale', $episode->podcast->language_code) + ->og('audio', $episode->audio_file_opengraph_url) + ->og('audio:type', $episode->audio_file_mimetype) + ->meta('article:published_time', $episode->published_at->format(DATE_ISO8601)) + ->meta('article:modified_time', $episode->updated_at->format(DATE_ISO8601)) + ->twitter('audio:partner', $episode->podcast->publisher ?? '') + ->twitter('audio:artist_name', $episode->podcast->owner_name) + ->twitter('player', $episode->getEmbedUrl('light')) + ->twitter('player:width', (string) config('Embed')->width) + ->twitter('player:height', (string) config('Embed')->height); + + if ($episode->podcast->payment_pointer) { + $metatags->meta('monetization', $episode->podcast->payment_pointer); + } + + return $metatags->__toString() . PHP_EOL . '<link rel="alternate" type="application/json+oembed" href="' . base_url( + route_to('episode-oembed-json', $episode->podcast->handle, $episode->slug) + ) . '" title="' . $episode->title . ' oEmbed json" />' . PHP_EOL . '<link rel="alternate" type="text/xml+oembed" href="' . base_url( + route_to('episode-oembed-xml', $episode->podcast->handle, $episode->slug) + ) . '" title="' . $episode->title . ' oEmbed xml" />' . PHP_EOL . $schema->__toString(); + } +} + +if (! function_exists('get_post_metatags')) { + function get_post_metatags(Post $post): string + { + $socialMediaPosting = new Thing('SocialMediaPosting', [ + '@id' => url_to('post', $post->actor->username, $post->id), + 'datePublished' => $post->published_at->format(DATE_ISO8601), + 'author' => new Thing('Person', [ + 'name' => $post->actor->display_name, + 'url' => $post->actor->uri, + ]), + 'text' => $post->message, + ]); + + if ($post->episode_id !== null) { + $socialMediaPosting->__set('sharedContent', new Thing('Audio', [ + 'headline' => $post->episode->title, + 'url' => $post->episode->link, + 'author' => new Thing('Person', [ + 'name' => $post->episode->podcast->owner_name, + ]), + ])); + } elseif ($post->preview_card !== null) { + $socialMediaPosting->__set('sharedContent', new Thing('WebPage', [ + 'headline' => $post->preview_card->title, + 'url' => $post->preview_card->url, + 'author' => new Thing('Person', [ + 'name' => $post->preview_card->author_name, + ]), + ])); + } + + $schema = new Schema($socialMediaPosting); + + $metatags = new MetaTags(); + $metatags + ->title(lang('Post.title', [ + 'actorDisplayName' => $post->actor->display_name, + ])) + ->description($post->message) + ->image($post->actor->avatar_image_url) + ->canonical((string) current_url()) + ->og('site_name', service('settings')->get('App.siteName')); + + return $metatags->__toString() . PHP_EOL . $schema->__toString(); + } +} + +if (! function_exists('get_episode_comment_metatags')) { + function get_episode_comment_metatags(EpisodeComment $episodeComment): string + { + $schema = new Schema(new Thing('SocialMediaPosting', [ + '@id' => url_to( + 'episode-comment', + $episodeComment->actor->username, + $episodeComment->episode->slug, + $episodeComment->id + ), + 'datePublished' => $episodeComment->created_at->format(DATE_ISO8601), + 'author' => new Thing('Person', [ + 'name' => $episodeComment->actor->display_name, + 'url' => $episodeComment->actor->uri, + ]), + 'text' => $episodeComment->message, + 'upvoteCount' => $episodeComment->likes_count, + ])); + + $metatags = new MetaTags(); + $metatags + ->title(lang('Comment.title', [ + 'actorDisplayName' => $episodeComment->actor->display_name, + 'episodeTitle' => $episodeComment->episode->title, + ])) + ->description($episodeComment->message) + ->image($episodeComment->actor->avatar_image_url) + ->canonical((string) current_url()) + ->og('site_name', service('settings')->get('App.siteName')); + + return $metatags->__toString() . PHP_EOL . $schema->__toString(); + } +} + +if (! function_exists('get_follow_metatags')) { + function get_follow_metatags(Actor $actor): string + { + $metatags = new MetaTags(); + $metatags + ->title(lang('Podcast.followTitle', [ + 'actorDisplayName' => $actor->display_name, + ])) + ->description($actor->summary) + ->image($actor->avatar_image_url) + ->canonical((string) current_url()) + ->og('site_name', service('settings')->get('App.siteName')); + + return $metatags->__toString(); + } +} + +if (! function_exists('get_remote_actions_metatags')) { + function get_remote_actions_metatags(Post $post, string $action): string + { + $metatags = new MetaTags(); + $metatags + ->title(lang('Fediverse.' . $action . '.title', [ + 'actorDisplayName' => $post->actor->display_name, + ],)) + ->description($post->message) + ->image($post->actor->avatar_image_url) + ->canonical((string) current_url()) + ->og('site_name', service('settings')->get('App.siteName')); + + return $metatags->__toString(); + } +} + +if (! function_exists('get_home_metatags')) { + function get_home_metatags(): string + { + $metatags = new MetaTags(); + $metatags + ->title(service('settings')->get('App.siteName')) + ->description(service('settings')->get('App.siteDescription')) + ->image(service('settings')->get('App.siteIcon')['512']) + ->canonical((string) current_url()) + ->og('site_name', service('settings')->get('App.siteName')); + + return $metatags->__toString(); + } +} + +if (! function_exists('get_page_metatags')) { + function get_page_metatags(Page $page): string + { + $metatags = new MetaTags(); + $metatags + ->title( + $page->title . service('settings')->get('App.siteTitleSeparator') . service( + 'settings' + )->get('App.siteName') + ) + ->description(service('settings')->get('App.siteDescription')) + ->image(service('settings')->get('App.siteIcon')['512']) + ->canonical((string) current_url()) + ->og('site_name', service('settings')->get('App.siteName')); + + return $metatags->__toString(); + } +} + +if (! function_exists('iso8601_duration')) { + // From https://stackoverflow.com/a/40761380 + function iso8601_duration(float $seconds): string + { + $days = floor($seconds / 86400); + $seconds %= 86400; + + $hours = floor($seconds / 3600); + $seconds %= 3600; + + $minutes = floor($seconds / 60); + $seconds %= 60; + + return sprintf('P%dDT%dH%dM%dS', $days, $hours, $minutes, $seconds); + } +} diff --git a/app/Language/en/Page.php b/app/Language/en/Page.php index 592b87846447755a83bdd76c16c0abcc67105313..9a255bda06546fb8d683d1d7f7f5cb9fecdead92 100644 --- a/app/Language/en/Page.php +++ b/app/Language/en/Page.php @@ -10,21 +10,8 @@ declare(strict_types=1); return [ 'back_to_home' => 'Back to home', - 'page' => 'Page', - 'all_pages' => 'All pages', - 'create' => 'New page', - 'go_to_page' => 'Go to page', - 'edit' => 'Edit page', - 'delete' => 'Delete page', - 'form' => [ - 'title' => 'Title', - 'permalink' => 'Permalink', - 'content' => 'Content', - 'submit_create' => 'Create page', - 'submit_edit' => 'Save', + 'map' => [ + 'title' => 'Map', + 'description' => 'Discover podcast episodes on {siteName} that are placed on a map! Travel through the map and listen to episodes that talk about specific locations.', ], - 'messages' => [ - 'createSuccess' => 'The page “{pageTitle}†was created successfully!', - ], - 'map' => 'Map', ]; diff --git a/app/Language/en/Podcast.php b/app/Language/en/Podcast.php index d8c0a20e483d7e67fc03ab8142e42d50426f65df..7528a9a3e2f4801db6ee2f4b1eea2110c3450113 100644 --- a/app/Language/en/Podcast.php +++ b/app/Language/en/Podcast.php @@ -28,8 +28,11 @@ return [ other {<span class="font-semibold">#</span> posts} }', 'activity' => 'Activity', + 'activity_title' => '{podcastTitle} news & activity', 'episodes' => 'Episodes', + 'episodes_title' => 'Episodes of {podcastTitle}', 'about' => 'About', + 'about_title' => 'About {podcastTitle}', 'sponsor_title' => 'Enjoying the show?', 'sponsor' => 'Sponsor', 'funding_links' => 'Funding links for {podcastTitle}', diff --git a/app/Language/fr/Page.php b/app/Language/fr/Page.php index 220b1a73efface001656efd6caff150b85d49969..8ad3f5d1134dfee336c0ebc5e6e1263c3919b1b9 100644 --- a/app/Language/fr/Page.php +++ b/app/Language/fr/Page.php @@ -10,21 +10,8 @@ declare(strict_types=1); return [ 'back_to_home' => 'Retour à l’accueil', - 'page' => 'Page', - 'all_pages' => 'Toutes les pages', - 'create' => 'Créer une page', - 'go_to_page' => 'Aller à la page', - 'edit' => 'Modifier la page', - 'delete' => 'Supprimer la page', - 'form' => [ - 'title' => 'Titre', - 'permalink' => 'Lien permanent', - 'content' => 'Contenu', - 'submit_create' => 'Créer la page', - 'submit_edit' => 'Enregistrer', + 'map' => [ + 'title' => 'Cartographie', + 'description' => 'Découvrez des épisodes de podcast placés sur une carte avec {siteName} ! Voyagez sur une carte du monde et écoutez des épisodes mentionnant des lieux spécifiques.', ], - 'messages' => [ - 'createSuccess' => 'La page {pageTitle} a été créée avec succès !', - ], - 'map' => 'Cartographie', ]; diff --git a/composer.json b/composer.json index b7dffcaf402d6e2ca6017f1da7ef98e4d48d66fe..a2f505883c9ade4225c7cb5f4ca79e6e54e866a5 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "michalsn/codeigniter4-uuid": "dev-develop", "essence/essence": "^3.5.4", "codeigniter4/settings": "dev-develop", - "chrisjean/php-ico": "^1.0" + "chrisjean/php-ico": "^1.0", + "melbahja/seo": "^2.0" }, "require-dev": { "mikey179/vfsstream": "^v1.6.8", diff --git a/composer.lock b/composer.lock index 82b1287a717d7bc55e249105cad9a44e797c9881..fabdde839b8b8d37b9f6b011336fc8108e1da711 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": "f35a050323bdc632cd550f9d13f0679c", + "content-hash": "c0a25c3d11c806b4bc62eafb22902bc8", "packages": [ { "name": "brick/math", @@ -1120,6 +1120,64 @@ }, "time": "2020-11-02T17:00:53+00:00" }, + { + "name": "melbahja/seo", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/melbahja/seo.git", + "reference": "a42500223cb532d4069e85097cc5b5e6ee402de1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/melbahja/seo/zipball/a42500223cb532d4069e85097cc5b5e6ee402de1", + "reference": "a42500223cb532d4069e85097cc5b5e6ee402de1", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-xml": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Melbahja\\Seo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Mohamed ELbahja", + "email": "mohamed@elbahja.me", + "homepage": "https://elbahja.me", + "role": "Developer" + } + ], + "description": "Simple PHP library to help developers 🻠do better on-page SEO optimization", + "keywords": [ + "PHP7", + "meta tags", + "open graph", + "php7.1", + "schema.org", + "search engine optimization", + "seo", + "sitemap index", + "sitemap.xml", + "sitemaps", + "twitter tags" + ], + "support": { + "issues": "https://github.com/melbahja/seo/issues", + "source": "https://github.com/melbahja/seo/tree/v2.0.0" + }, + "time": "2021-10-26T00:36:49+00:00" + }, { "name": "michalsn/codeigniter4-uuid", "version": "dev-develop", diff --git a/themes/cp_admin/_layout.php b/themes/cp_admin/_layout.php index 11e602136aa98f56209c263ef9b3fa4ed42fbdca..9cc6cdc062c98fbdef63ccd3cbd226a4baccb7f8 100644 --- a/themes/cp_admin/_layout.php +++ b/themes/cp_admin/_layout.php @@ -4,6 +4,8 @@ <head> <meta charset="UTF-8"/> + <meta name="robots" content="noindex"> + <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"/> diff --git a/themes/cp_admin/episode/_sidebar.php b/themes/cp_admin/episode/_sidebar.php index 96d21e343f95181cc9201cc24ff27d10e1273036..af4c2e3bfa3f27fc9a2a5771505687fdb7032ac9 100644 --- a/themes/cp_admin/episode/_sidebar.php +++ b/themes/cp_admin/episode/_sidebar.php @@ -16,7 +16,7 @@ $podcastNavigation = [ /> <span class="flex-1 w-full px-2 text-xs font-semibold truncate" title="<?= $podcast->title ?>"><?= $podcast->title ?></span> </a> -<div class="flex items-center px-4 py-2 border-t border-b border-navigation"> +<div class="flex items-center px-4 py-2 border-y border-navigation"> <img src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" diff --git a/themes/cp_admin/episode/publish.php b/themes/cp_admin/episode/publish.php index 692483e4bc1d319eca9cc286550853ace6b7750f..a0397e536f3cb383923e053501f4ddc57677d869 100644 --- a/themes/cp_admin/episode/publish.php +++ b/themes/cp_admin/episode/publish.php @@ -39,7 +39,7 @@ <div class="px-4 mb-2"> <Forms.Textarea name="message" placeholder="<?= lang('Episode.publish_form.message_placeholder') ?>" autofocus="" rows="2" /> </div> - <div class="flex border-t border-b"> + <div class="flex border-y"> <img src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> diff --git a/themes/cp_admin/episode/publish_edit.php b/themes/cp_admin/episode/publish_edit.php index 17f0162cc83ef7d83a368951b55e18ead0e90649..6e38016e91358e8013c544b80d2209d7d2025615 100644 --- a/themes/cp_admin/episode/publish_edit.php +++ b/themes/cp_admin/episode/publish_edit.php @@ -41,7 +41,7 @@ <div class="px-4 mb-2"> <Forms.Textarea name="message" placeholder="<?= lang('Episode.publish_form.message_placeholder') ?>" autofocus="" value="<?= $post->message ?>" rows="2" /> </div> - <div class="flex border-t border-b"> + <div class="flex border-y"> <img src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> diff --git a/themes/cp_app/embed.php b/themes/cp_app/embed.php index 0f4b3464730ef4046dc7fce23ae481dfb7fd7cb9..e81ac9e962e1a83771e237b6282d03135e560c4a 100644 --- a/themes/cp_app/embed.php +++ b/themes/cp_app/embed.php @@ -4,6 +4,7 @@ <head> <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title><?= $episode->title ?></title> <meta name="description" content="<?= htmlspecialchars( $episode->description, diff --git a/themes/cp_app/episode/_layout.php b/themes/cp_app/episode/_layout.php index 0142abdc8006a15dd47fd6788439d16f75fc0242..16d340d2efc790589b1f1dff2948503e447a4158 100644 --- a/themes/cp_app/episode/_layout.php +++ b/themes/cp_app/episode/_layout.php @@ -12,10 +12,7 @@ <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): ?> - <meta name="monetization" content="<?= $podcast->payment_pointer ?>" /> - <?php endif; ?> + <?= $metatags ?> <?= service('vite') ->asset('styles/index.css', 'css') ?> diff --git a/themes/cp_app/episode/_partials/preview_card.php b/themes/cp_app/episode/_partials/preview_card.php index c0966e56869ca2231bd0cb19a0b1e86e7dbe4c22..d429d938df4cde6867d90fd1288ddd4a60c7f86c 100644 --- a/themes/cp_app/episode/_partials/preview_card.php +++ b/themes/cp_app/episode/_partials/preview_card.php @@ -1,4 +1,4 @@ -<div class="flex items-center border-t border-b border-subtle"> +<div class="flex items-center border-y border-subtle"> <div class="relative"> <time class="absolute px-1 text-sm font-semibold text-white rounded bg-black/75 bottom-2 right-2" datetime="PT<?= $episode->audio_file_duration ?>S"> <?= format_duration($episode->audio_file_duration) ?> diff --git a/themes/cp_app/episode/activity.php b/themes/cp_app/episode/activity.php index c0ca08aa779ae6fa202c42ee9abe4e763beedf54..5c9a8d7e009b84da01676c95711c8d56acc4e074 100644 --- a/themes/cp_app/episode/activity.php +++ b/themes/cp_app/episode/activity.php @@ -1,36 +1,5 @@ <?= $this->extend('episode/_layout') ?> -<?= $this->section('meta-tags') ?> -<title><?= $episode->title ?></title> -<meta name="description" content="<?= htmlspecialchars($episode->description) ?>" /> -<link rel="canonical" href="<?= $episode->link ?>" /> -<meta property="og:title" content="<?= $episode->title ?>" /> -<meta property="og:description" content="<?= $episode->description ?>" /> -<meta property="og:locale" content="<?= $podcast->language_code ?>" /> -<meta property="og:site_name" content="<?= $podcast->title ?>" /> -<meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $episode->cover->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->podcastCoverSizes['large'][0] ?>" /> -<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> -<meta property="og:description" content="$description" /> -<meta property="article:published_time" content="<?= $episode->published_at ?>" /> -<meta property="article:modified_time" content="<?= $episode->updated_at ?>" /> -<meta property="og:audio" content="<?= $episode->audio_file_opengraph_url ?>" /> -<meta property="og:audio:type" content="<?= $episode->audio_file_mimetype ?>" /> -<link rel="alternate" type="application/json+oembed" href="<?= base_url(route_to('episode-oembed-json', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed json" /> -<link rel="alternate" type="text/xml+oembed" href="<?= base_url(route_to('episode-oembed-xml', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed xml" /> -<meta name="twitter:title" content="<?= $episode->title ?>" /> -<meta name="twitter:description" content="<?= $episode->description ?>" /> -<meta name="twitter:image" content="<?= $episode->cover->large_url ?>" /> -<meta name="twitter:card" content="player" /> -<meta property="twitter:audio:partner" content="<?= $podcast->publisher ?>" /> -<meta property="twitter:audio:artist_name" content="<?= $podcast->owner_name ?>" /> -<meta name="twitter:player" content="<?= $episode->getEmbedUrl('light') ?>" /> -<meta name="twitter:player:width" content="600" /> -<meta name="twitter:player:height" content="200" /> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <?php if (can_user_interact()): ?> diff --git a/themes/cp_app/episode/comment.php b/themes/cp_app/episode/comment.php index 37c1e2364cdbe577a121f78d42bb976e6115ce85..446bfb143fd2f308dd7bdcbf000a4ccc6b9dc96f 100644 --- a/themes/cp_app/episode/comment.php +++ b/themes/cp_app/episode/comment.php @@ -1,30 +1,13 @@ <?= $this->extend('episode/_layout') ?> -<?= $this->section('meta-tags') ?> - <title><?= lang('Comment.title', [ - 'actorDisplayName' => $comment->actor->display_name, - ]) ?></title> - <meta name="description" content="<?= $comment->message ?>"/> - <meta property="og:title" content="<?= lang('Comment.title', [ - 'actorDisplayName' => $comment->actor->display_name, - ]) ?>"/> - <meta property="og:locale" content="<?= service( - 'request', - )->getLocale() ?>" /> - <meta property="og:site_name" content="<?= $comment->actor->display_name ?>" /> - <meta property="og:url" content="<?= current_url() ?>" /> - <meta property="og:image" content="<?= $comment->actor->avatar_image_url ?>" /> - <meta property="og:description" content="<?= $comment->message ?>" /> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <div class="max-w-2xl px-6 mx-auto"> <nav class="mb-2"> <a href="<?= route_to('episode', $podcast->handle, $episode->slug) ?>" class="inline-flex items-center px-4 py-2 text-sm focus:ring-accent"><?= icon( - 'arrow-left', - 'mr-2 text-lg', - ) . lang('Comment.back_to_comments') ?></a> + 'arrow-left', + 'mr-2 text-lg', +) . lang('Comment.back_to_comments') ?></a> </nav> <div class="pb-12"> <?= $this->include('episode/_partials/comment_with_replies') ?> diff --git a/themes/cp_app/episode/comments.php b/themes/cp_app/episode/comments.php index 873e166874913915f05a5224ad6dbdb19af4272a..be862832c79561adeb6d4f28403d8b9e3b60692d 100644 --- a/themes/cp_app/episode/comments.php +++ b/themes/cp_app/episode/comments.php @@ -1,38 +1,5 @@ <?= $this->extend('episode/_layout') ?> -<?= $this->section('meta-tags') ?> -<title><?= $episode->title ?></title> -<meta name="description" content="<?= htmlspecialchars( - $episode->description, -) ?>" /> -<link rel="canonical" href="<?= $episode->link ?>" /> -<meta property="og:title" content="<?= $episode->title ?>" /> -<meta property="og:description" content="<?= $episode->description ?>" /> -<meta property="og:locale" content="<?= $podcast->language_code ?>" /> -<meta property="og:site_name" content="<?= $podcast->title ?>" /> -<meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $episode->cover->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->podcastCoverSizes['large'][0] ?>" /> -<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> -<meta property="og:description" content="$description" /> -<meta property="article:published_time" content="<?= $episode->published_at ?>" /> -<meta property="article:modified_time" content="<?= $episode->updated_at ?>" /> -<meta property="og:audio" content="<?= $episode->audio_file_opengraph_url ?>" /> -<meta property="og:audio:type" content="<?= $episode->audio_file_mimetype ?>" /> -<link rel="alternate" type="application/json+oembed" href="<?= base_url(route_to('episode-oembed-json', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed json" /> -<link rel="alternate" type="text/xml+oembed" href="<?= base_url(route_to('episode-oembed-xml', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed xml" /> -<meta name="twitter:title" content="<?= $episode->title ?>" /> -<meta name="twitter:description" content="<?= $episode->description ?>" /> -<meta name="twitter:image" content="<?= $episode->cover->large_url ?>" /> -<meta name="twitter:card" content="player" /> -<meta property="twitter:audio:partner" content="<?= $podcast->publisher ?>" /> -<meta property="twitter:audio:artist_name" content="<?= $podcast->owner_name ?>" /> -<meta name="twitter:player" content="<?= $episode->getEmbedUrl('light') ?>" /> -<meta name="twitter:player:width" content="600" /> -<meta name="twitter:player:height" content="200" /> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <?php if (can_user_interact()): ?> diff --git a/themes/cp_app/home.php b/themes/cp_app/home.php index f15b2b9e60b711717597be366b7a7fd8ac390574..708cfe331ad3476b2ef42001b14c8d07a75188dd 100644 --- a/themes/cp_app/home.php +++ b/themes/cp_app/home.php @@ -15,12 +15,7 @@ <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') ?>" /> + <?= $metatags ?> <?= service('vite') ->asset('styles/index.css', 'css') ?> diff --git a/themes/cp_app/page.php b/themes/cp_app/page.php deleted file mode 100644 index 148f3152fc996702f7984714d82f34d3b7ba3e5c..0000000000000000000000000000000000000000 --- a/themes/cp_app/page.php +++ /dev/null @@ -1,48 +0,0 @@ -<?= helper('page') ?> -<!DOCTYPE html> -<html lang="<?= service('request') - ->getLocale() ?>"> - -<head> - <meta charset="UTF-8"/> - <title><?= $page->title ?></title> - <meta name="viewport" content="width=device-width, initial-scale=1.0"/> - <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') - ->asset('js/app.ts', 'js') ?> -</head> - -<body class="flex flex-col min-h-screen mx-auto bg-base theme-<?= service('settings') - ->get('App.theme') ?>"> - <?php if (service('authentication')->check()): ?> - <?= $this->include('_admin_navbar') ?> - <?php endif; ?> - - <header class="py-8 border-b bg-elevated border-subtle"> - <div class="container flex flex-col items-start px-2 py-4 mx-auto"> - <a href="<?= route_to('home') ?>" - class="inline-flex items-center mb-2 focus:ring-accent"><?= icon( - 'arrow-left', - 'mr-2', - ) . lang('Page.back_to_home') ?></a> - <Heading tagName="h1" class="text-3xl font-semibold"><?= $page->title ?></Heading> - </div> - </header> - <main class="container flex-1 px-4 py-10 mx-auto"> - <div class="prose prose-brand"> - <?= $page->content_html ?> - </div> - </main> - <footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t border-subtle"> - <?= render_page_links() ?> - <small><?= lang('Common.powered_by', [ - 'castopod' => - '<a class="inline-flex font-semibold hover:underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>', - ]) ?></small> - </footer> -</body> diff --git a/themes/cp_app/_layout.php b/themes/cp_app/pages/_layout.php similarity index 76% rename from themes/cp_app/_layout.php rename to themes/cp_app/pages/_layout.php index d80ecb993a914b3fdd44788da5bcb2096b371891..30f9177c76e4ed45b9e21d6594ed1799574d965f 100644 --- a/themes/cp_app/_layout.php +++ b/themes/cp_app/pages/_layout.php @@ -5,21 +5,13 @@ <head> <meta charset="UTF-8"/> - <title><?= $this->renderSection('title') ?></title> - <meta name="description" content="<?= service('settings') - ->get('App.siteDescription') ?>"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <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') ?>" /> + <?= $metatags ?> <?= service('vite') ->asset('styles/index.css', 'css') ?> @@ -42,9 +34,7 @@ 'arrow-left', 'mr-2', ) . lang('Page.back_to_home') ?></a> - <Heading tagName="h1" size="large"><?= isset($page) - ? $page->title - : 'Castopod' ?></Heading> + <Heading tagName="h1" size="large"><?= $page->title ?></Heading> </div> </header> <main class="container flex-1 px-4 py-6 mx-auto"> diff --git a/themes/cp_app/credits.php b/themes/cp_app/pages/credits.php similarity index 98% rename from themes/cp_app/credits.php rename to themes/cp_app/pages/credits.php index 44127527bf4fcc90debe9bda70430ad369c14bfd..a050bc1265ddd7ac341c84e10c75e0f842986118 100644 --- a/themes/cp_app/credits.php +++ b/themes/cp_app/pages/credits.php @@ -1,4 +1,4 @@ -<?= $this->extend('_layout') ?> +<?= $this->extend('pages/_layout') ?> <?= $this->section('title') ?> <?= lang('Person.credits') ?> diff --git a/themes/cp_app/map.php b/themes/cp_app/pages/map.php similarity index 85% rename from themes/cp_app/map.php rename to themes/cp_app/pages/map.php index f064a54fdfc2c170a363900fef642dbfc95cb185..7f7b966103bdb3e63d4191f0f82be3b89a96697f 100644 --- a/themes/cp_app/map.php +++ b/themes/cp_app/pages/map.php @@ -5,13 +5,17 @@ <head> <meta charset="UTF-8"/> - <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."/> + <title><?= lang('Page.map.title') . service('settings')->get('App.siteTitleSeparator') . service('settings')->get('App.siteName') ?></title> + <meta name="description" content="<?= lang('Page.map.description', [ + 'siteName' => service('settings') + ->get('App.siteName'), + ]) ?>"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <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') @@ -33,7 +37,7 @@ 'arrow-left', 'mr-2', ) . lang('Page.back_to_home') ?></a> - <Heading tagName="h1" size="large"><?= lang('Page.map') ?></Heading> + <Heading tagName="h1" size="large"><?= lang('Page.map.title') ?></Heading> </div> </header> <main class="flex-1 w-full h-full"> diff --git a/themes/cp_app/pages/page.php b/themes/cp_app/pages/page.php new file mode 100644 index 0000000000000000000000000000000000000000..430d75941eee2166c2dbbec9953abec9f8393829 --- /dev/null +++ b/themes/cp_app/pages/page.php @@ -0,0 +1,11 @@ +<?= $this->extend('pages/_layout') ?> + +<?= $this->section('title') ?> +<?= lang('Person.credits') ?> +<?= $this->endSection() ?> + +<?= $this->section('content') ?> + <div class="prose prose-brand"> + <?= $page->content_html ?> + </div> +<?= $this->endSection() ?> \ No newline at end of file diff --git a/themes/cp_app/podcast/_layout.php b/themes/cp_app/podcast/_layout.php index cf38f6d0efaff9bd1a12980bf0b1bd9ab51bbb90..4c4c812bdc62566bc67934509e80b3481b505eb2 100644 --- a/themes/cp_app/podcast/_layout.php +++ b/themes/cp_app/podcast/_layout.php @@ -12,10 +12,7 @@ <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): ?> - <meta name="monetization" content="<?= $podcast->payment_pointer ?>" /> - <?php endif; ?> + <?= $metatags ?> <?= service('vite') ->asset('styles/index.css', 'css') ?> diff --git a/themes/cp_app/podcast/about.php b/themes/cp_app/podcast/about.php index ce00cbb1177604d3f486528798798d3e2bf9acc9..0beb5e1e480b4b668065e34326e4b6b7fe205595 100644 --- a/themes/cp_app/podcast/about.php +++ b/themes/cp_app/podcast/about.php @@ -1,33 +1,5 @@ <?= $this->extend('podcast/_layout') ?> -<?= $this->section('meta-tags') ?> -<!-- TODO: --> - -<link type="application/rss+xml" rel="alternate" title="<?= $podcast->title ?>" href="<?= $podcast->feed_url ?>" /> - -<title><?= $podcast->title ?></title> -<meta name="description" content="<?= htmlspecialchars( - $podcast->description, -) ?>" /> -<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 ?>" /> -<meta property="og:locale" content="<?= $podcast->language_code ?>" /> -<meta property="og:site_name" content="<?= $podcast->title ?>" /> -<meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> -<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> -<meta name="twitter:card" content="summary_large_image" /> - -<?= service('vite') - ->asset('styles/index.css', 'css') ?> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <div class="px-2 sm:px-4"> @@ -35,14 +7,14 @@ <div class="flex gap-x-4 gap-y-2"> <span class="px-2 py-1 text-sm font-semibold border rounded-sm border-subtle bg-highlight"> <?= lang( - 'Podcast.category_options.' . $podcast->category->code, - ) ?> + 'Podcast.category_options.' . $podcast->category->code, +) ?> </span> <?php foreach ($podcast->other_categories as $other_category): ?> <span class="px-2 py-1 text-sm font-semibold border rounded-sm border-subtle bg-highlight"> <?= lang( - 'Podcast.category_options.' . $other_category->code, - ) ?> + 'Podcast.category_options.' . $other_category->code, +) ?> </span> <?php endforeach; ?> </div> @@ -55,8 +27,8 @@ <?php foreach ($podcast->persons as $person): ?> <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-background-base last:ml-0" /> <?php $i++; if ($i === 3) { - break; - }?> + break; +}?> <?php endforeach; ?> </div> <?= lang('Podcast.persons', [ diff --git a/themes/cp_app/podcast/activity.php b/themes/cp_app/podcast/activity.php index a30afc4dfdd087ac76e791374fdde3ccde54ebdd..4ca98903656919f1130142caabd7ffd05c7615ca 100644 --- a/themes/cp_app/podcast/activity.php +++ b/themes/cp_app/podcast/activity.php @@ -1,31 +1,5 @@ <?= $this->extend('podcast/_layout') ?> -<?= $this->section('meta-tags') ?> -<link type="application/rss+xml" rel="alternate" title="<?= $podcast->title ?>" href="<?= $podcast->feed_url ?>"/> - -<title><?= $podcast->title ?></title> -<meta name="description" content="<?= htmlspecialchars( - $podcast->description, -) ?>" /> -<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 ?>" /> -<meta property="og:locale" content="<?= $podcast->language_code ?>" /> -<meta property="og:site_name" content="<?= $podcast->title ?>" /> -<meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> -<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> -<meta name="twitter:card" content="summary_large_image" /> - -<?= service('vite') - ->asset('styles/index.css', 'css') ?> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <?php if (can_user_interact()): ?> @@ -53,6 +27,7 @@ <hr class="my-4 border-subtle"> <?php endif; ?> + <div class="flex flex-col gap-y-4"> <?php foreach ($posts as $key => $post): ?> <?php if ($post->reblog_of_id !== null): ?> diff --git a/themes/cp_app/podcast/episodes.php b/themes/cp_app/podcast/episodes.php index 7e5b01f428b15ab925dda34ed430002ab6ea2b09..1db4ec29a86c36fe7dece9f9fba7b0eb33555d50 100644 --- a/themes/cp_app/podcast/episodes.php +++ b/themes/cp_app/podcast/episodes.php @@ -1,31 +1,5 @@ <?= $this->extend('podcast/_layout') ?> -<?= $this->section('meta-tags') ?> -<link type="application/rss+xml" rel="alternate" title="<?= $podcast->title ?>" href="<?= $podcast->feed_url ?>" /> - -<title><?= $podcast->title ?></title> -<meta name="description" content="<?= htmlspecialchars( - $podcast->description, -) ?>" /> -<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 ?>" /> -<meta property="og:locale" content="<?= $podcast->language_code ?>" /> -<meta property="og:site_name" content="<?= $podcast->title ?>" /> -<meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> -<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> -<meta name="twitter:card" content="summary_large_image" /> - -<?= service('vite') - ->asset('styles/index.css', 'css') ?> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <?php if ($episodes): ?> diff --git a/themes/cp_app/podcast/follow.php b/themes/cp_app/podcast/follow.php index c491d46e098d99e516595ee7753258d0afa94d8c..0425544dee5a923d7f0789cdade76b82ee70eff6 100644 --- a/themes/cp_app/podcast/follow.php +++ b/themes/cp_app/podcast/follow.php @@ -12,19 +12,7 @@ <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, - ]) ?></title> - <meta name="description" content="<?= $actor->summary ?>"/> - <meta property="og:title" content="<?= lang('Podcast.followTitle', [ - 'actorDisplayName' => $actor->display_name, - ]) ?>"/> - <meta property="og:locale" content="<?= service( - 'request', - )->getLocale() ?>" /> - <meta property="og:url" content="<?= current_url() ?>" /> - <meta property="og:image" content="<?= $actor->avatar_image_url ?>" /> - <meta property="og:description" content="<?= $actor->summary ?>" /> + <?= $metatags ?> <?= service('vite') ->asset('styles/index.css', 'css') ?> @@ -32,7 +20,6 @@ ->asset('js/podcast.ts', 'js') ?> </head> - <body class="flex flex-col min-h-screen bg-base theme-<?= service('settings') ->get('App.theme') ?>"> <header class="flex flex-col items-center mb-8"> diff --git a/themes/cp_app/post/post.php b/themes/cp_app/post/post.php index 1605e3fbad870e717e869072f08cc2816738f94a..02c168bf73dd382a01db3121471a1810362b0540 100644 --- a/themes/cp_app/post/post.php +++ b/themes/cp_app/post/post.php @@ -1,29 +1,12 @@ <?= $this->extend('podcast/_layout') ?> -<?= $this->section('meta-tags') ?> - <title><?= lang('Post.title', [ - 'actorDisplayName' => $post->actor->display_name, - ]) ?></title> - <meta name="description" content="<?= $post->message ?>"/> - <meta property="og:title" content="<?= lang('Post.title', [ - 'actorDisplayName' => $post->actor->display_name, - ]) ?>"/> - <meta property="og:locale" content="<?= service( - 'request', - )->getLocale() ?>" /> - <meta property="og:site_name" content="<?= $post->actor->display_name ?>" /> - <meta property="og:url" content="<?= current_url() ?>" /> - <meta property="og:image" content="<?= $post->actor->avatar_image_url ?>" /> - <meta property="og:description" content="<?= $post->message ?>" /> -<?= $this->endSection() ?> - <?= $this->section('content') ?> <nav class="py-2"> <a href="<?= route_to('podcast-activity', $podcast->handle) ?>" class="inline-flex items-center px-4 py-2 text-sm focus:ring-accent"><?= icon( - 'arrow-left', - 'mr-2 text-lg', - ) . + 'arrow-left', + 'mr-2 text-lg', +) . lang('Post.back_to_actor_posts', [ 'actor' => $post->actor->display_name, ]) ?></a> diff --git a/themes/cp_app/post/remote_action.php b/themes/cp_app/post/remote_action.php index 2dd36fe500006e1b0c05dc44e834a8e08b3e36d1..c0d13fd46802bbf7591c36063e6ecac7f9d1739c 100644 --- a/themes/cp_app/post/remote_action.php +++ b/themes/cp_app/post/remote_action.php @@ -10,23 +10,7 @@ <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, - ]) ?></title> - <meta name="description" content="<?= $post->message ?>"/> - <meta property="og:title" content="<?= lang( - 'Fediverse.' . $action . '.title', - [ - 'actorDisplayName' => $post->actor->display_name, - ], - ) ?>"/> - <meta property="og:locale" content="<?= service( - 'request', - )->getLocale() ?>" /> - <meta property="og:site_name" content="<?= $post->actor->display_name ?>" /> - <meta property="og:url" content="<?= current_url() ?>" /> - <meta property="og:image" content="<?= $post->actor->avatar_image_url ?>" /> - <meta property="og:description" content="<?= $post->message ?>" /> + <?= $metatags ?> <?= service('vite') ->asset('styles/index.css', 'css') ?> diff --git a/themes/cp_auth/_layout.php b/themes/cp_auth/_layout.php index 1ac2151e8101bd888d2d0ae0e7d721ef15d09c10..fe6c3bfafefb33ddfa72e6ca2105ae3f8f9162da 100644 --- a/themes/cp_auth/_layout.php +++ b/themes/cp_auth/_layout.php @@ -4,6 +4,8 @@ <head> <meta charset="UTF-8"/> + <meta name="robots" content="noindex"> + <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"/> diff --git a/themes/cp_install/_layout.php b/themes/cp_install/_layout.php index 0de02688cba342f70280b2bd030d1638537692cd..0f9ee0d9415e0c960eda65551e6213414dfa1b04 100644 --- a/themes/cp_install/_layout.php +++ b/themes/cp_install/_layout.php @@ -3,7 +3,9 @@ <head> <meta charset="UTF-8"/> - <title>Castopod</title> + <meta name="robots" content="noindex"> + + <title>Castopod Install</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="icon" type="image/x-icon" href="<?= service('settings')