feat: add schema.org json-ld objects to podcasts, episodes, posts and comments pages
- refactor meta-tags by generating them in the controller and injecting them into the views - use `melbahja/seo` library to build opengraph and twitter meta-tags + schema.org objects
This commit is contained in:
parent
5c529a83aa
commit
902f959b30
|
@ -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.';
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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', [
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,6 +36,7 @@ class HomeController extends BaseController
|
|||
|
||||
// default behavior: list all podcasts on home page
|
||||
$data = [
|
||||
'metatags' => get_home_metatags(),
|
||||
'podcasts' => $allPodcasts,
|
||||
];
|
||||
|
||||
|
|
|
@ -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,
|
||||
]);
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
];
|
||||
|
|
|
@ -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}',
|
||||
|
|
|
@ -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',
|
||||
];
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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 ?>"
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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') ?>
|
||||
|
|
|
@ -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) ?>
|
||||
|
|
|
@ -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()): ?>
|
||||
|
|
|
@ -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') ?>
|
||||
|
|
|
@ -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()): ?>
|
||||
|
|
|
@ -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') ?>
|
||||
|
|
|
@ -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>
|
|
@ -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">
|
|
@ -1,4 +1,4 @@
|
|||
<?= $this->extend('_layout') ?>
|
||||
<?= $this->extend('pages/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Person.credits') ?>
|
|
@ -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">
|
|
@ -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() ?>
|
|
@ -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') ?>
|
||||
|
|
|
@ -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', [
|
||||
|
|
|
@ -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): ?>
|
||||
|
|
|
@ -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): ?>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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') ?>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue