ci: fix lint and formatting issues

This commit is contained in:
Yassine Doghri 2023-02-22 16:29:45 +00:00
parent 34777598dd
commit 84a6447fd4
94 changed files with 987 additions and 1104 deletions

View File

@ -1 +1 @@
{"version":1,"defects":[],"times":{"Tests\\Database\\ExampleDatabaseTest::testModelFindAll":0.003,"Tests\\Database\\ExampleDatabaseTest::testSoftDeleteLeavesRow":0.004,"modules\\Api\\Rest\\V1\\PodcastTest::testList":0.033,"modules\\Api\\Rest\\V1\\PodcastTest::testView":0.004,"modules\\Api\\Rest\\V1\\PodcastTest::testViewNotFound":0.01,"modules\\Api\\Rest\\V1\\PodcastTest::testListEmpty":4.313,"Tests\\Session\\ExampleSessionTest::testSessionSimple":0,"HealthTest::testIsDefinedAppPath":0,"HealthTest::testBaseUrlHasBeenSet":0.005}} {"version":1,"defects":[],"times":{"Tests\\Database\\ExampleDatabaseTest::testModelFindAll":0.002,"Tests\\Database\\ExampleDatabaseTest::testSoftDeleteLeavesRow":0.002,"modules\\Api\\Rest\\V1\\PodcastTest::testList":0.008,"modules\\Api\\Rest\\V1\\PodcastTest::testView":0.003,"modules\\Api\\Rest\\V1\\PodcastTest::testViewNotFound":0.002,"modules\\Api\\Rest\\V1\\PodcastTest::testListEmpty":0.633,"Tests\\Session\\ExampleSessionTest::testSessionSimple":0,"HealthTest::testIsDefinedAppPath":0,"HealthTest::testBaseUrlHasBeenSet":0.001}}

View File

@ -49,8 +49,6 @@ class Database extends Config
/** /**
* This database connection is used when running PHPUnit database tests. * This database connection is used when running PHPUnit database tests.
* *
* @noRector StringClassNameToClassConstantRector
*
* @var array<string, mixed> * @var array<string, mixed>
*/ */
public array $tests = [ public array $tests = [
@ -84,7 +82,6 @@ class Database extends Config
// Ensure that we always set the database group to 'tests' if // Ensure that we always set the database group to 'tests' if
// we are currently running an automated test suite, so that // we are currently running an automated test suite, so that
// we don't overwrite live data on accident. // we don't overwrite live data on accident.
/** @noRector RemoveAlwaysTrueIfConditionRector */
if (ENVIRONMENT === 'testing') { if (ENVIRONMENT === 'testing') {
$this->defaultGroup = 'tests'; $this->defaultGroup = 'tests';
} }

View File

@ -7,6 +7,7 @@ namespace Config;
use App\Entities\Actor; use App\Entities\Actor;
use App\Entities\Post; use App\Entities\Post;
use App\Models\EpisodeModel; use App\Models\EpisodeModel;
use CodeIgniter\Debug\Toolbar\Collectors\Database;
use CodeIgniter\Events\Events; use CodeIgniter\Events\Events;
use CodeIgniter\Exceptions\FrameworkException; use CodeIgniter\Exceptions\FrameworkException;
@ -47,7 +48,7 @@ Events::on('pre_system', static function () {
* If you delete, they will no longer be collected. * If you delete, they will no longer be collected.
*/ */
if (CI_DEBUG && ! is_cli()) { if (CI_DEBUG && ! is_cli()) {
Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); Events::on('DBQuery', Database::class . '::collect');
Services::toolbar()->respond(); Services::toolbar()->respond();
} }
}); });

View File

@ -21,8 +21,6 @@ class Pager extends BaseConfig
* and the desired group as $pagerGroup; * and the desired group as $pagerGroup;
* *
* @var array<string, string> * @var array<string, string>
*
* @noRector Rector\Php55\Rector\String_\StringClassNameToClassConstantRector
*/ */
public $templates = [ public $templates = [
'default_full' => 'App\Views\pager\default_full', 'default_full' => 'App\Views\pager\default_full',

View File

@ -27,8 +27,6 @@ class Services extends BaseService
/** /**
* The Router class uses a RouteCollection's array of routes, and determines the correct Controller and Method to * The Router class uses a RouteCollection's array of routes, and determines the correct Controller and Method to
* execute. * execute.
*
* @noRector PHPStan\Reflection\MissingMethodFromReflectionException
*/ */
public static function router( public static function router(
?RouteCollectionInterface $routes = null, ?RouteCollectionInterface $routes = null,
@ -48,8 +46,6 @@ class Services extends BaseService
/** /**
* The Negotiate class provides the content negotiation features for working the request to determine correct * The Negotiate class provides the content negotiation features for working the request to determine correct
* language, encoding, charset, and more. * language, encoding, charset, and more.
*
* @noRector PHPStan\Reflection\MissingMethodFromReflectionException
*/ */
public static function negotiator(?RequestInterface $request = null, bool $getShared = true): Negotiate public static function negotiator(?RequestInterface $request = null, bool $getShared = true): Negotiate
{ {

View File

@ -15,9 +15,6 @@ use CodeIgniter\HTTP\Response;
class ColorsController extends Controller class ColorsController extends Controller
{ {
/**
* @noRector ReturnTypeDeclarationRector
*/
public function index(): Response public function index(): Response
{ {
$cacheName = 'colors.css'; $cacheName = 'colors.css';

View File

@ -74,7 +74,7 @@ class EpisodeAudioController extends Controller
if ( if (
($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null
) { ) {
throw PageNotFoundException::forPageNotFound(); throw PageNotFoundException::forPageNotFound();
} }

View File

@ -55,7 +55,7 @@ class EpisodeCommentController extends BaseController
if ( if (
($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null
) { ) {
throw PageNotFoundException::forPageNotFound(); throw PageNotFoundException::forPageNotFound();
} }
@ -120,9 +120,6 @@ class EpisodeCommentController extends BaseController
return $cachedView; return $cachedView;
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function commentObject(): Response public function commentObject(): Response
{ {
$commentObject = new CommentObject($this->comment); $commentObject = new CommentObject($this->comment);
@ -132,9 +129,6 @@ class EpisodeCommentController extends BaseController
->setBody($commentObject->toJSON()); ->setBody($commentObject->toJSON());
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function replies(): Response public function replies(): Response
{ {
/** /**

View File

@ -51,7 +51,7 @@ class EpisodeController extends BaseController
if ( if (
($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null
) { ) {
throw PageNotFoundException::forPageNotFound(); throw PageNotFoundException::forPageNotFound();
} }
@ -274,9 +274,6 @@ class EpisodeController extends BaseController
return $this->response->setXML($oembed); return $this->response->setXML($oembed);
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function episodeObject(): Response public function episodeObject(): Response
{ {
$podcastObject = new PodcastEpisode($this->episode); $podcastObject = new PodcastEpisode($this->episode);
@ -286,9 +283,6 @@ class EpisodeController extends BaseController
->setBody($podcastObject->toJSON()); ->setBody($podcastObject->toJSON());
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function comments(): Response public function comments(): Response
{ {
/** /**

View File

@ -47,9 +47,6 @@ class PodcastController extends BaseController
return $this->{$method}(...$params); return $this->{$method}(...$params);
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function podcastActor(): Response public function podcastActor(): Response
{ {
$podcastActor = new PodcastActor($this->podcast); $podcastActor = new PodcastActor($this->podcast);
@ -285,9 +282,6 @@ class PodcastController extends BaseController
return $cachedView; return $cachedView;
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function episodeCollection(): Response public function episodeCollection(): Response
{ {
if ($this->podcast->type === 'serial') { if ($this->podcast->type === 'serial') {

View File

@ -45,7 +45,7 @@ class PostController extends FediversePostController
public function _remap(string $method, string ...$params): mixed public function _remap(string $method, string ...$params): mixed
{ {
if ( if (
($podcast = (new PodcastModel())->getPodcastByHandle($params[0],)) === null ($podcast = (new PodcastModel())->getPodcastByHandle($params[0])) === null
) { ) {
throw PageNotFoundException::forPageNotFound(); throw PageNotFoundException::forPageNotFound();
} }

View File

@ -36,9 +36,6 @@ class Category extends Entity
'google_category' => 'string', 'google_category' => 'string',
]; ];
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getParent(): ?self public function getParent(): ?self
{ {
if ($this->parent_id === null) { if ($this->parent_id === null) {

View File

@ -92,9 +92,6 @@ class Credit extends Entity
return $this->episode; return $this->episode;
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getGroupLabel(): string public function getGroupLabel(): string
{ {
if ($this->person_group === null) { if ($this->person_group === null) {
@ -104,9 +101,6 @@ class Credit extends Entity
return lang("PersonsTaxonomy.persons.{$this->person_group}.label"); return lang("PersonsTaxonomy.persons.{$this->person_group}.label");
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getRoleLabel(): string public function getRoleLabel(): string
{ {
if ($this->person_group === '') { if ($this->person_group === '') {

View File

@ -450,7 +450,7 @@ class Episode extends Entity
public function getEmbedUrl(string $theme = null): string public function getEmbedUrl(string $theme = null): string
{ {
return $theme return $theme
? url_to('embed-theme', esc($this->getPodcast()->handle), esc($this->attributes['slug']), $theme,) ? url_to('embed-theme', esc($this->getPodcast()->handle), esc($this->attributes['slug']), $theme)
: url_to('embed', esc($this->getPodcast()->handle), esc($this->attributes['slug'])); : url_to('embed', esc($this->getPodcast()->handle), esc($this->attributes['slug']));
} }

View File

@ -87,8 +87,6 @@ class EpisodeComment extends UuidEntity
/** /**
* Returns the comment's actor * Returns the comment's actor
*
* @noRector ReturnTypeDeclarationRector
*/ */
public function getActor(): ?Actor public function getActor(): ?Actor
{ {
@ -125,9 +123,6 @@ class EpisodeComment extends UuidEntity
return $this->getReplies() !== []; return $this->getReplies() !== [];
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getReplyToComment(): ?self public function getReplyToComment(): ?self
{ {
if ($this->in_reply_to_id === null) { if ($this->in_reply_to_id === null) {

View File

@ -215,9 +215,6 @@ class Podcast extends Entity
return '@' . $this->handle; return '@' . $this->handle;
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getActor(): ?Actor public function getActor(): ?Actor
{ {
if ($this->actor_id === 0) { if ($this->actor_id === 0) {

View File

@ -61,7 +61,7 @@ if (! function_exists('parse_form_attributes')) {
} }
} }
if (! empty($attributes)) { if ($attributes !== []) {
$default = array_merge($default, $attributes); $default = array_merge($default, $attributes);
} }
} }

View File

@ -239,7 +239,6 @@ if (! function_exists('generate_random_salt')) {
if (! function_exists('file_upload_max_size')) { if (! function_exists('file_upload_max_size')) {
/** /**
* Returns a file size limit in bytes based on the PHP upload_max_filesize and post_max_size Adapted from: * Returns a file size limit in bytes based on the PHP upload_max_filesize and post_max_size Adapted from:
* https://stackoverflow.com/a/25370978 * https://stackoverflow.com/a/25370978

View File

@ -125,7 +125,7 @@ if (! function_exists('get_rss_feed')) {
$castopodSocialElement->addAttribute('accountUrl', $podcast->link); $castopodSocialElement->addAttribute('accountUrl', $podcast->link);
foreach ($podcast->social_platforms as $socialPlatform) { foreach ($podcast->social_platforms as $socialPlatform) {
$socialElement = $channel->addChild('social', null, $podcastNamespace,); $socialElement = $channel->addChild('social', null, $podcastNamespace);
$socialElement->addAttribute('priority', '2'); $socialElement->addAttribute('priority', '2');
$socialElement->addAttribute('platform', $socialPlatform->slug); $socialElement->addAttribute('platform', $socialPlatform->slug);
@ -202,7 +202,7 @@ if (! function_exists('get_rss_feed')) {
foreach ($podcast->persons as $person) { foreach ($podcast->persons as $person) {
foreach ($person->roles as $role) { foreach ($person->roles as $role) {
$personElement = $channel->addChild('person', $person->full_name, $podcastNamespace,); $personElement = $channel->addChild('person', $person->full_name, $podcastNamespace);
$personElement->addAttribute('img', $person->avatar->medium_url); $personElement->addAttribute('img', $person->avatar->medium_url);
@ -294,7 +294,7 @@ if (! function_exists('get_rss_feed')) {
$item->addChild('guid', $episode->guid); $item->addChild('guid', $episode->guid);
$item->addChild('pubDate', $episode->published_at->format(DATE_RFC1123)); $item->addChild('pubDate', $episode->published_at->format(DATE_RFC1123));
if ($episode->location !== null) { if ($episode->location !== null) {
$locationElement = $item->addChild('location', $episode->location->name, $podcastNamespace,); $locationElement = $item->addChild('location', $episode->location->name, $podcastNamespace);
if ($episode->location->geo !== null) { if ($episode->location->geo !== null) {
$locationElement->addAttribute('geo', $episode->location->geo); $locationElement->addAttribute('geo', $episode->location->geo);
} }
@ -376,11 +376,11 @@ if (! function_exists('get_rss_feed')) {
foreach ($episode->persons as $person) { foreach ($episode->persons as $person) {
foreach ($person->roles as $role) { foreach ($person->roles as $role) {
$personElement = $item->addChild('person', esc($person->full_name), $podcastNamespace,); $personElement = $item->addChild('person', esc($person->full_name), $podcastNamespace);
$personElement->addAttribute( $personElement->addAttribute(
'role', 'role',
esc(lang("PersonsTaxonomy.persons.{$role->group}.roles.{$role->role}.label", [], 'en'),), esc(lang("PersonsTaxonomy.persons.{$role->group}.roles.{$role->role}.label", [], 'en')),
); );
$personElement->addAttribute( $personElement->addAttribute(

View File

@ -30,7 +30,6 @@ class Router extends CodeIgniterRouter
*/ */
protected function checkRoutes(string $uri): bool protected function checkRoutes(string $uri): bool
{ {
/** @noRector RemoveExtraParametersRector */
$routes = $this->collection->getRoutes($this->collection->getHTTPVerb()); $routes = $this->collection->getRoutes($this->collection->getHTTPVerb());
// Don't waste any time // Don't waste any time

View File

@ -86,7 +86,6 @@ class TranscriptParser
} }
break; break;
} }
} }

View File

@ -70,7 +70,6 @@ class Vite
if (array_key_exists('imports', $manifestElement)) { if (array_key_exists('imports', $manifestElement)) {
foreach ($manifestElement['imports'] as $importPath) { foreach ($manifestElement['imports'] as $importPath) {
if (array_key_exists($importPath, $this->manifestData)) { if (array_key_exists($importPath, $this->manifestData)) {
// import css dependencies if any // import css dependencies if any
if (array_key_exists('css', $this->manifestData[$importPath])) { if (array_key_exists('css', $this->manifestData[$importPath])) {
foreach ($this->manifestData[$importPath]['css'] as $cssFile) { foreach ($this->manifestData[$importPath]['css'] as $cssFile) {
@ -98,11 +97,11 @@ class Vite
'css' => <<<CODE_SAMPLE 'css' => <<<CODE_SAMPLE
<link rel="stylesheet" href="{$assetUrl}"/> <link rel="stylesheet" href="{$assetUrl}"/>
CODE_SAMPLE CODE_SAMPLE
, ,
'js' => <<<CODE_SAMPLE 'js' => <<<CODE_SAMPLE
<script type="module" src="{$assetUrl}"></script> <script type="module" src="{$assetUrl}"></script>
CODE_SAMPLE CODE_SAMPLE
, ,
default => '', default => '',
}; };
} }

View File

@ -53,9 +53,6 @@ class ClipModel extends Model
'job_ended_at', 'job_ended_at',
]; ];
/**
* @noRector
*/
protected $returnType = BaseClip::class; protected $returnType = BaseClip::class;
/** /**

View File

@ -195,8 +195,6 @@ class EpisodeCommentModel extends UuidModel
* Retrieves all published posts for a given episode ordered by publication date * Retrieves all published posts for a given episode ordered by publication date
* *
* @return EpisodeComment[] * @return EpisodeComment[]
*
* @noRector ReturnTypeDeclarationRector
*/ */
public function getEpisodeComments(int $episodeId): array public function getEpisodeComments(int $episodeId): array
{ {

View File

@ -28,9 +28,6 @@ class MediaModel extends Model
*/ */
protected $table = 'media'; protected $table = 'media';
/**
* @noRector
*/
protected $returnType = Document::class; protected $returnType = Document::class;
/** /**
@ -135,8 +132,6 @@ class MediaModel extends Model
/** /**
* @param Document|Audio|Video|Image|Transcript|Chapters $media * @param Document|Audio|Video|Image|Transcript|Chapters $media
*
* @noRector ReturnTypeDeclarationRector
*/ */
public function saveMedia(object $media): int | false public function saveMedia(object $media): int | false
{ {
@ -150,8 +145,6 @@ class MediaModel extends Model
/** /**
* @param Document|Audio|Video|Image|Transcript|Chapters $media * @param Document|Audio|Video|Image|Transcript|Chapters $media
*
* @noRector ReturnTypeDeclarationRector
*/ */
public function updateMedia(object $media): bool public function updateMedia(object $media): bool
{ {

View File

@ -70,7 +70,7 @@ $error_id = uniqid('error', true); ?>
} else { } else {
echo esc(clean_path($row['file']) . ' : ' . $row['line']); echo esc(clean_path($row['file']) . ' : ' . $row['line']);
} }
?> ?>
<?php else : ?> <?php else : ?>
{PHP internal code} {PHP internal code}
<?php endif; ?> <?php endif; ?>
@ -85,14 +85,14 @@ $error_id = uniqid('error', true); ?>
<table cellspacing="0"> <table cellspacing="0">
<?php <?php
$params = null; $params = null;
// Reflection by name is not available for closure function // Reflection by name is not available for closure function
if (substr($row['function'], -1) !== '}') { if (substr($row['function'], -1) !== '}') {
$mirror = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']); $mirror = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']);
$params = $mirror->getParameters(); $params = $mirror->getParameters();
} }
foreach ($row['args'] as $key => $value) : ?> foreach ($row['args'] as $key => $value) : ?>
<tr> <tr>
<td><code><?= esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?></code></td> <td><code><?= esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?></code></td>
<td><pre><?= esc(print_r($value, true)) ?></pre></td> <td><pre><?= esc(print_r($value, true)) ?></pre></td>
@ -310,8 +310,8 @@ $error_id = uniqid('error', true); ?>
<!-- Response --> <!-- Response -->
<?php <?php
$response = \Config\Services::response(); $response = \Config\Services::response();
$response->setStatusCode(http_response_code()); $response->setStatusCode(http_response_code());
?> ?>
<div class="content" id="response"> <div class="content" id="response">
<table> <table>
<tr> <tr>

View File

@ -11,18 +11,18 @@ $pager->setSurroundCount(2);
<?php if ($pager->hasPreviousPage()): ?> <?php if ($pager->hasPreviousPage()): ?>
<li> <li>
<a href="<?= $pager->getFirst() ?>" aria-label="<?= lang( <a href="<?= $pager->getFirst() ?>" aria-label="<?= lang(
'Pager.first', 'Pager.first',
) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight"> ) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight">
<span aria-hidden="true"><?= lang('Pager.first') ?></span> <span aria-hidden="true"><?= lang('Pager.first') ?></span>
</a> </a>
</li> </li>
<li> <li>
<a href="<?= $pager->getPreviousPage() ?>" aria-label="<?= lang( <a href="<?= $pager->getPreviousPage() ?>" aria-label="<?= lang(
'Pager.previous', 'Pager.previous',
) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight"> ) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight">
<span aria-hidden="true"><?= lang( <span aria-hidden="true"><?= lang(
'Pager.previous', 'Pager.previous',
) ?></span> ) ?></span>
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>
@ -34,9 +34,7 @@ $pager->setSurroundCount(2);
<?= $link['title'] ?> <?= $link['title'] ?>
</span> </span>
<?php else: ?> <?php else: ?>
<a href="<?= $link[ <a href="<?= $link['uri'] ?>" class="block px-4 py-2 rounded-full text-skin-muted hover:bg-highlight">
'uri'
] ?>" class="block px-4 py-2 rounded-full text-skin-muted hover:bg-highlight">
<?= $link['title'] ?> <?= $link['title'] ?>
</a> </a>
<?php endif; ?> <?php endif; ?>
@ -46,15 +44,15 @@ $pager->setSurroundCount(2);
<?php if ($pager->hasNextPage()): ?> <?php if ($pager->hasNextPage()): ?>
<li> <li>
<a href="<?= $pager->getNextPage() ?>" aria-label="<?= lang( <a href="<?= $pager->getNextPage() ?>" aria-label="<?= lang(
'Pager.next', 'Pager.next',
) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight"> ) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight">
<span aria-hidden="true"><?= lang('Pager.next') ?></span> <span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a> </a>
</li> </li>
<li> <li>
<a href="<?= $pager->getLast() ?>" aria-label="<?= lang( <a href="<?= $pager->getLast() ?>" aria-label="<?= lang(
'Pager.last', 'Pager.last',
) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight"> ) ?>" class="block px-3 py-2 text-skin-muted hover:bg-highlight">
<span aria-hidden="true"><?= lang('Pager.last') ?></span> <span aria-hidden="true"><?= lang('Pager.last') ?></span>
</a> </a>
</li> </li>

View File

@ -89,7 +89,7 @@ class PodcastPlatformController extends BaseController
array_key_exists('visible', $podcastPlatform) && array_key_exists('visible', $podcastPlatform) &&
$podcastPlatform['visible'] === 'yes', $podcastPlatform['visible'] === 'yes',
'is_on_embed' => 'is_on_embed' =>
array_key_exists('on_embed', $podcastPlatform,) && $podcastPlatform['on_embed'] === 'yes', array_key_exists('on_embed', $podcastPlatform) && $podcastPlatform['on_embed'] === 'yes',
]; ];
} }

View File

@ -47,7 +47,6 @@ class SchedulerController extends Controller
// Loop through clips to generate them // Loop through clips to generate them
foreach ($scheduledClips as $scheduledClip) { foreach ($scheduledClips as $scheduledClip) {
try { try {
// set clip to pending // set clip to pending
(new ClipModel()) (new ClipModel())
->update($scheduledClip->id, [ ->update($scheduledClip->id, [

View File

@ -117,7 +117,7 @@ class SoundbiteController extends BaseController
$newSoundbite = new Soundbite([ $newSoundbite = new Soundbite([
'title' => $this->request->getPost('title'), 'title' => $this->request->getPost('title'),
'start_time' => (float) $this->request->getPost('start_time'), 'start_time' => (float) $this->request->getPost('start_time'),
'duration' => (float) $this->request->getPost('duration',), 'duration' => (float) $this->request->getPost('duration'),
'type' => 'audio', 'type' => 'audio',
'status' => '', 'status' => '',
'podcast_id' => $this->podcast->id, 'podcast_id' => $this->podcast->id,

View File

@ -165,7 +165,7 @@ class VideoClipsController extends BaseController
$videoClip = new VideoClip([ $videoClip = new VideoClip([
'title' => $this->request->getPost('title'), 'title' => $this->request->getPost('title'),
'start_time' => (float) $this->request->getPost('start_time'), 'start_time' => (float) $this->request->getPost('start_time'),
'duration' => (float) $this->request->getPost('duration',), 'duration' => (float) $this->request->getPost('duration'),
'theme' => $theme, 'theme' => $theme,
'format' => $this->request->getPost('format'), 'format' => $this->request->getPost('format'),
'type' => 'video', 'type' => 'video',

View File

@ -57,8 +57,8 @@ $routes->group('', [
/** /**
* @deprecated Route for podcast audio file analytics (/audio/pack(podcast_id,episode_id,bytes_threshold,filesize,duration,date)/podcast_folder/filename.mp3) * @deprecated Route for podcast audio file analytics (/audio/pack(podcast_id,episode_id,bytes_threshold,filesize,duration,date)/podcast_folder/filename.mp3)
*/ */
$routes->head('audio/(:base64)/(:any)', 'EpisodeAnalyticsController::hit/$1/$2',); $routes->head('audio/(:base64)/(:any)', 'EpisodeAnalyticsController::hit/$1/$2');
$routes->get('audio/(:base64)/(:any)', 'EpisodeAnalyticsController::hit/$1/$2',); $routes->get('audio/(:base64)/(:any)', 'EpisodeAnalyticsController::hit/$1/$2');
}); });
// Show the Unknown UserAgents // Show the Unknown UserAgents

View File

@ -39,9 +39,6 @@ class AnalyticsPodcastsByCountry extends Entity
'hits' => 'integer', 'hits' => 'integer',
]; ];
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getLabels(): string public function getLabels(): string
{ {
return lang('Countries.' . $this->attributes['labels']); return lang('Countries.' . $this->attributes['labels']);

View File

@ -44,9 +44,6 @@ class AnalyticsPodcastsByRegion extends Entity
'hits' => 'integer', 'hits' => 'integer',
]; ];
/**
* @noRector ReturnTypeDeclarationRector
*/
public function getCountryCode(): string public function getCountryCode(): string
{ {
return lang('Countries.' . $this->attributes['country_code']); return lang('Countries.' . $this->attributes['country_code']);

View File

@ -15,9 +15,6 @@ use CodeIgniter\HTTP\Response;
class ActivityPubController extends Controller class ActivityPubController extends Controller
{ {
/**
* @noRector ReturnTypeDeclarationRector
*/
public function preflight(): Response public function preflight(): Response
{ {
return $this->response->setHeader('Access-Control-Allow-Origin', '*') // for allowing any domain, insecure return $this->response->setHeader('Access-Control-Allow-Origin', '*') // for allowing any domain, insecure

View File

@ -56,9 +56,6 @@ class ActorController extends Controller
return $this->{$method}(...$params); return $this->{$method}(...$params);
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function index(): ResponseInterface public function index(): ResponseInterface
{ {
$actorObjectClass = $this->config->actorObject; $actorObjectClass = $this->config->actorObject;
@ -71,8 +68,6 @@ class ActorController extends Controller
/** /**
* Handles incoming requests from fediverse servers * Handles incoming requests from fediverse servers
*
* @noRector ReturnTypeDeclarationRector
*/ */
public function inbox(): ResponseInterface public function inbox(): ResponseInterface
{ {
@ -264,9 +259,6 @@ class ActorController extends Controller
} }
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function outbox(): ResponseInterface public function outbox(): ResponseInterface
{ {
// get published activities by publication date // get published activities by publication date
@ -297,9 +289,6 @@ class ActorController extends Controller
->setBody($collection->toJSON()); ->setBody($collection->toJSON());
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function followers(): ResponseInterface public function followers(): ResponseInterface
{ {
$tablesPrefix = config('Fediverse') $tablesPrefix = config('Fediverse')
@ -386,9 +375,6 @@ class ActorController extends Controller
); );
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function activity(string $activityId): ResponseInterface public function activity(string $activityId): ResponseInterface
{ {
if ( if (

View File

@ -53,9 +53,6 @@ class PostController extends Controller
return $this->{$method}(...$params); return $this->{$method}(...$params);
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function index(): Response public function index(): Response
{ {
$noteObjectClass = $this->config->noteObject; $noteObjectClass = $this->config->noteObject;
@ -66,9 +63,6 @@ class PostController extends Controller
->setBody($noteObject->toJSON()); ->setBody($noteObject->toJSON());
} }
/**
* @noRector ReturnTypeDeclarationRector
*/
public function replies(): Response public function replies(): Response
{ {
/** /**

View File

@ -539,7 +539,7 @@ class PostModel extends BaseUuidModel
->first(); ->first();
$announceActivity = new AnnounceActivity($reblogPost); $announceActivity = new AnnounceActivity($reblogPost);
$announceActivity->set('id', url_to('activity', $reblogPost->actor->username, $activity->id),); $announceActivity->set('id', url_to('activity', $reblogPost->actor->username, $activity->id));
$undoActivity $undoActivity
->set('actor', $reblogPost->actor->uri) ->set('actor', $reblogPost->actor->uri)

View File

@ -115,19 +115,13 @@ class InstallController extends Controller
} }
try { try {
$db = db_connect();
// Check if instance owner has been created, meaning install was completed // Check if instance owner has been created, meaning install was completed
if ( if ((new UserModel())->where('is_owner', true)
$db->tableExists('users') && ->first() !== null
(new UserModel())->where('is_owner', true) ) {
->first() !== null
) {
// if so, show a 404 page // if so, show a 404 page
throw PageNotFoundException::forPageNotFound(); throw PageNotFoundException::forPageNotFound();
} }
/** @noRector */
} catch (DatabaseException) { } catch (DatabaseException) {
// Could not connect to the database // Could not connect to the database
// show database config view to fix value // show database config view to fix value

View File

@ -42,9 +42,6 @@ class SubscriptionModel extends Model
'updated_by', 'updated_by',
]; ];
/**
* @noRector
*/
protected $returnType = Subscription::class; protected $returnType = Subscription::class;
/** /**

View File

@ -20,7 +20,6 @@ class WebSubController extends Controller
{ {
public function publish(): void public function publish(): void
{ {
/** @noRector RemoveAlwaysTrueIfConditionRector */
if (ENVIRONMENT !== 'production') { if (ENVIRONMENT !== 'production') {
return; return;
} }

View File

@ -46,12 +46,9 @@ class PodcastTest extends CIUnitTestCase
private readonly string $podcastApiUrl; private readonly string $podcastApiUrl;
/** public function __construct(?string $name = null)
* @param array<mixed> $data
*/
public function __construct(?string $name = null, array $data = [], $dataName = '')
{ {
parent::__construct($name, $data, $dataName); parent::__construct($name);
$this->podcast = FakeSinglePodcastApiSeeder::podcast(); $this->podcast = FakeSinglePodcastApiSeeder::podcast();
$this->podcast['created_at'] = []; $this->podcast['created_at'] = [];
$this->podcast['updated_at'] = []; $this->podcast['updated_at'] = [];

View File

@ -8,13 +8,13 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
class="h-full pr-1 text-xl md:hidden focus:ring-accent focus:ring-inset" aria-label="<?= lang('Navigation.toggle_sidebar') ?>"><?= icon('menu') ?></button> class="h-full pr-1 text-xl md:hidden focus:ring-accent focus:ring-inset" aria-label="<?= lang('Navigation.toggle_sidebar') ?>"><?= icon('menu') ?></button>
<div class="inline-flex items-center h-full"> <div class="inline-flex items-center h-full">
<a href="<?= route_to( <a href="<?= route_to(
'admin', 'admin',
) ?>" class="inline-flex items-center h-full px-2 border-r border-navigation focus:ring-inset focus:ring-accent"> ) ?>" class="inline-flex items-center h-full px-2 border-r border-navigation focus:ring-inset focus:ring-accent">
<?= (isset($podcast) ? icon('arrow-left', 'mr-2') : '') . svg('castopod-logo-base', 'h-6') ?> <?= (isset($podcast) ? icon('arrow-left', 'mr-2') : '') . svg('castopod-logo-base', 'h-6') ?>
</a> </a>
<a href="<?= route_to( <a href="<?= route_to(
'home', 'home',
) ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold hover:underline focus:ring-inset focus:ring-accent"> ) ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold hover:underline focus:ring-inset focus:ring-accent">
<?= lang('Navigation.go_to_website') ?> <?= lang('Navigation.go_to_website') ?>
<?= icon('external-link', 'ml-1 opacity-60') ?> <?= icon('external-link', 'ml-1 opacity-60') ?>
</a> </a>
@ -27,26 +27,26 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<?php endif ?> <?php endif ?>
</button> </button>
<?php <?php
$notificationsTitle = lang('Notifications.title'); $notificationsTitle = lang('Notifications.title');
$items = [ $items = [
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span> <span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span>
CODE_SAMPLE), CODE_SAMPLE),
], ],
]; ];
if ($userPodcasts !== []) { if ($userPodcasts !== []) {
foreach ($userPodcasts as $userPodcast) { foreach ($userPodcasts as $userPodcast) {
$userPodcastTitle = esc($userPodcast->title); $userPodcastTitle = esc($userPodcast->title);
$unreadNotificationDotDisplayClass = in_array($userPodcast->actor_id, $actorIdsWithUnreadNotifications, true) ? '' : 'hidden'; $unreadNotificationDotDisplayClass = in_array($userPodcast->actor_id, $actorIdsWithUnreadNotifications, true) ? '' : 'hidden';
$items[] = [ $items[] = [
'type' => 'link', 'type' => 'link',
'title' => <<<CODE_SAMPLE 'title' => <<<CODE_SAMPLE
<div class="inline-flex items-center flex-1 text-sm align-middle"> <div class="inline-flex items-center flex-1 text-sm align-middle">
<div class="relative"> <div class="relative">
<img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /> <img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" />
@ -55,20 +55,20 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<span class="max-w-xs truncate">{$userPodcastTitle}</span> <span class="max-w-xs truncate">{$userPodcastTitle}</span>
</div> </div>
CODE_SAMPLE CODE_SAMPLE
, ,
'uri' => route_to('notification-list', $userPodcast->id), 'uri' => route_to('notification-list', $userPodcast->id),
]; ];
} }
} else { } else {
$noNotificationsText = lang('Notifications.no_notifications'); $noNotificationsText = lang('Notifications.no_notifications');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span> <span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span>
CODE_SAMPLE), CODE_SAMPLE),
]; ];
} }
?> ?>
<DropdownMenu id="notifications-dropdown-menu" labelledby="notifications-dropdown" items="<?= esc(json_encode($items)) ?>" placement="bottom"/> <DropdownMenu id="notifications-dropdown-menu" labelledby="notifications-dropdown" items="<?= esc(json_encode($items)) ?>" placement="bottom"/>
<button <button
@ -86,48 +86,48 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<?= icon('caret-down', 'ml-auto text-2xl') ?></button> <?= icon('caret-down', 'ml-auto text-2xl') ?></button>
</div> </div>
<?php <?php
$interactButtons = ''; $interactButtons = '';
foreach ($userPodcasts as $userPodcast) { foreach ($userPodcasts as $userPodcast) {
$checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : ''; $checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : '';
$userPodcastTitle = esc($userPodcast->title); $userPodcastTitle = esc($userPodcast->title);
$interactButtons .= <<<CODE_SAMPLE $interactButtons .= <<<CODE_SAMPLE
<button class="inline-flex items-center w-full px-4 py-1 hover:bg-highlight" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}"> <button class="inline-flex items-center w-full px-4 py-1 hover:bg-highlight" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}">
<div class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /><span class="max-w-xs truncate">{$userPodcastTitle}</span>{$checkMark}</div> <div class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /><span class="max-w-xs truncate">{$userPodcastTitle}</span>{$checkMark}</div>
</button> </button>
CODE_SAMPLE; CODE_SAMPLE;
} }
$interactAsText = lang('Common.choose_interact'); $interactAsText = lang('Common.choose_interact');
$interactAsRoute = route_to('interact-as-actor'); $interactAsRoute = route_to('interact-as-actor');
$csrfField = csrf_field(); $csrfField = csrf_field();
$menuItems = [ $menuItems = [
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Navigation.account.my-account'), 'title' => lang('Navigation.account.my-account'),
'uri' => route_to('my-account'), 'uri' => route_to('my-account'),
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Navigation.account.change-password'), 'title' => lang('Navigation.account.change-password'),
'uri' => route_to('change-password'), 'uri' => route_to('change-password'),
], ],
[ [
'type' => 'separator', 'type' => 'separator',
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Navigation.account.logout'), 'title' => lang('Navigation.account.logout'),
'uri' => route_to('logout'), 'uri' => route_to('logout'),
], ],
]; ];
if ($userPodcasts !== []) { if ($userPodcasts !== []) {
$menuItems = array_merge([ $menuItems = array_merge([
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<nav class="flex flex-col py-2 whitespace-nowrap"> <nav class="flex flex-col py-2 whitespace-nowrap">
<span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span> <span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span>
<form action="{$interactAsRoute}" method="POST" class="flex flex-col"> <form action="{$interactAsRoute}" method="POST" class="flex flex-col">
@ -136,12 +136,12 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
</form> </form>
</nav> </nav>
CODE_SAMPLE), CODE_SAMPLE),
], ],
[ [
'type' => 'separator', 'type' => 'separator',
], ],
], $menuItems); ], $menuItems);
} }
?> ?>
<DropdownMenu id="my-account-dropdown-menu" labelledby="my-account-dropdown" items="<?= esc(json_encode($menuItems)) ?>" /> <DropdownMenu id="my-account-dropdown-menu" labelledby="my-account-dropdown" items="<?= esc(json_encode($menuItems)) ?>" />
</header> </header>

View File

@ -53,23 +53,23 @@
'type' => 'separator', 'type' => 'separator',
], ],
]; ];
if ($episode->published_at === null) { if ($episode->published_at === null) {
$items[] = [ $items[] = [
'type' => 'link', 'type' => 'link',
'title' => lang('Episode.delete'), 'title' => lang('Episode.delete'),
'uri' => route_to('episode-delete', $episode->podcast->id, $episode->id), 'uri' => route_to('episode-delete', $episode->podcast->id, $episode->id),
'class' => 'font-semibold text-red-600', 'class' => 'font-semibold text-red-600',
]; ];
} else { } else {
$label = lang('Episode.delete'); $label = lang('Episode.delete');
$icon = icon('forbid', 'mr-2'); $icon = icon('forbid', 'mr-2');
$title = lang('Episode.messages.unpublishBeforeDeleteTip'); $title = lang('Episode.messages.unpublishBeforeDeleteTip');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}{$label}</span> <span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}{$label}</span>
CODE_SAMPLE), CODE_SAMPLE),
]; ];
} ?> } ?>
<DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode($items)) ?>" /> <DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode($items)) ?>" />
</article> </article>

View File

@ -34,12 +34,12 @@ $podcastNavigation = [
<div class="flex flex-col items-start flex-1 w-48 px-2"> <div class="flex flex-col items-start flex-1 w-48 px-2">
<span class="w-full font-semibold truncate" title="<?= esc($episode->title) ?>"><?= esc($episode->title) ?></span> <span class="w-full font-semibold truncate" title="<?= esc($episode->title) ?>"><?= esc($episode->title) ?></span>
<a href="<?= route_to( <a href="<?= route_to(
'episode', 'episode',
esc($podcast->handle), esc($podcast->handle),
esc($episode->slug), esc($episode->slug),
) ?>" class="inline-flex items-center text-xs hover:underline focus:ring-accent"><?= lang( ) ?>" class="inline-flex items-center text-xs hover:underline focus:ring-accent"><?= lang(
'EpisodeNavigation.go_to_page', 'EpisodeNavigation.go_to_page',
) ?> ) ?>
<?= icon('external-link', 'ml-1 opacity-60') ?> <?= icon('external-link', 'ml-1 opacity-60') ?>
</a> </a>
</div> </div>
@ -49,19 +49,19 @@ $podcastNavigation = [
<div> <div>
<button class="inline-flex items-center w-full px-4 py-1 font-semibold focus:ring-accent" type="button"> <button class="inline-flex items-center w-full px-4 py-1 font-semibold focus:ring-accent" type="button">
<?= icon($data['icon'], 'opacity-60 text-2xl mr-4') . <?= icon($data['icon'], 'opacity-60 text-2xl mr-4') .
lang('EpisodeNavigation.' . $section) ?> lang('EpisodeNavigation.' . $section) ?>
</button> </button>
<ul class="flex flex-col"> <ul class="flex flex-col">
<?php foreach ($data['items'] as $item): ?> <?php foreach ($data['items'] as $item): ?>
<?php $isActive = url_is(route_to($item, $podcast->id, $episode->id)); ?> <?php $isActive = url_is(route_to($item, $podcast->id, $episode->id)); ?>
<li class="inline-flex"> <li class="inline-flex">
<a class="w-full py-1 pl-14 pr-2 text-sm hover:opacity-100 focus:ring-inset focus:ring-accent <?= $isActive <a class="w-full py-1 pl-14 pr-2 text-sm hover:opacity-100 focus:ring-inset focus:ring-accent <?= $isActive
? 'font-semibold opacity-100 inline-flex items-center' ? 'font-semibold opacity-100 inline-flex items-center'
: 'opacity-75' ?>" href="<?= route_to( : 'opacity-75' ?>" href="<?= route_to(
$item, $item,
$podcast->id, $podcast->id,
$episode->id $episode->id
) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . lang('EpisodeNavigation.' . $item) ?></a> ) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . lang('EpisodeNavigation.' . $item) ?></a>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
</ul> </ul>

View File

@ -32,7 +32,7 @@
name="cover" name="cover"
label="<?= lang('Episode.form.cover') ?>" label="<?= lang('Episode.form.cover') ?>"
hint="<?= lang('Episode.form.cover_hint') ?>" hint="<?= lang('Episode.form.cover_hint') ?>"
helper="<?= lang('Episode.form.cover_size_hint', ) ?>" helper="<?= lang('Episode.form.cover_size_hint') ?>"
type="file" type="file"
accept=".jpg,.jpeg,.png" /> accept=".jpg,.jpeg,.png" />

View File

@ -172,13 +172,13 @@
<?php if ($episode->transcript) : ?> <?php if ($episode->transcript) : ?>
<div class="flex items-center mb-1 gap-x-2"> <div class="flex items-center mb-1 gap-x-2">
<?= anchor( <?= anchor(
$episode->transcript->file_url, $episode->transcript->file_url,
icon('file-download', 'mr-1 text-skin-muted text-xl') . lang('Episode.form.transcript_download'), icon('file-download', 'mr-1 text-skin-muted text-xl') . lang('Episode.form.transcript_download'),
[ [
'class' => 'flex-1 font-semibold hover:underline inline-flex items-center text-xs', 'class' => 'flex-1 font-semibold hover:underline inline-flex items-center text-xs',
'download' => '', 'download' => '',
], ],
) . ) .
anchor( anchor(
route_to( route_to(
'transcript-delete', 'transcript-delete',
@ -227,13 +227,13 @@
<?php if ($episode->chapters) : ?> <?php if ($episode->chapters) : ?>
<div class="flex mb-1 gap-x-2"> <div class="flex mb-1 gap-x-2">
<?= anchor( <?= anchor(
$episode->chapters->file_url, $episode->chapters->file_url,
icon('file-download', 'mr-1 text-skin-muted text-xl') . lang('Episode.form.chapters_download'), icon('file-download', 'mr-1 text-skin-muted text-xl') . lang('Episode.form.chapters_download'),
[ [
'class' => 'flex-1 font-semibold hover:underline inline-flex items-center text-xs', 'class' => 'flex-1 font-semibold hover:underline inline-flex items-center text-xs',
'download' => '', 'download' => '',
], ],
) . ) .
anchor( anchor(
route_to( route_to(
'chapters-delete', 'chapters-delete',

View File

@ -47,11 +47,11 @@
<div class="flex items-baseline"> <div class="flex items-baseline">
<span class="flex-1 w-0 mr-2 text-sm font-semibold truncate"><?= esc($episode->title) ?></span> <span class="flex-1 w-0 mr-2 text-sm font-semibold truncate"><?= esc($episode->title) ?></span>
<?= episode_numbering( <?= episode_numbering(
$episode->number, $episode->number,
$episode->season_number, $episode->season_number,
'text-xs font-semibold text-skin-muted !no-underline border px-1 border-gray-500', 'text-xs font-semibold text-skin-muted !no-underline border px-1 border-gray-500',
true, true,
) ?> ) ?>
</div> </div>
<div class="text-xs text-skin-muted"> <div class="text-xs text-skin-muted">
<time datetime="PT<?= round($episode->audio->duration, 3) ?>S"> <time datetime="PT<?= round($episode->audio->duration, 3) ?>S">

View File

@ -49,11 +49,11 @@
<div class="flex items-baseline"> <div class="flex items-baseline">
<span class="flex-1 w-0 mr-2 text-sm font-semibold truncate"><?= esc($episode->title) ?></span> <span class="flex-1 w-0 mr-2 text-sm font-semibold truncate"><?= esc($episode->title) ?></span>
<?= episode_numbering( <?= episode_numbering(
$episode->number, $episode->number,
$episode->season_number, $episode->season_number,
'text-xs font-semibold text-skin-muted !no-underline border px-1 border-gray-500', 'text-xs font-semibold text-skin-muted !no-underline border px-1 border-gray-500',
true, true,
) ?> ) ?>
</div> </div>
<div class="text-xs text-skin-muted"> <div class="text-xs text-skin-muted">
<?= relative_time($episode->published_at) ?> <?= relative_time($episode->published_at) ?>
@ -76,8 +76,8 @@
<?php if ($podcast->publication_status === 'published'): ?> <?php if ($podcast->publication_status === 'published'): ?>
<fieldset class="flex flex-col"> <fieldset class="flex flex-col">
<legend class="text-lg font-semibold"><?= lang( <legend class="text-lg font-semibold"><?= lang(
'Episode.publish_form.publication_date', 'Episode.publish_form.publication_date',
) ?></legend> ) ?></legend>
<Forms.Radio value="now" name="publication_method" isChecked="<?= old('publication_method') && old('publish') === 'now' ?>"><?= lang('Episode.publish_form.publication_method.now') ?></Forms.Radio> <Forms.Radio value="now" name="publication_method" isChecked="<?= old('publication_method') && old('publish') === 'now' ?>"><?= lang('Episode.publish_form.publication_method.now') ?></Forms.Radio>
<div class="inline-flex flex-wrap items-center radio-toggler"> <div class="inline-flex flex-wrap items-center radio-toggler">
<input <input

View File

@ -41,20 +41,20 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.XY title="<?= lang('Charts.episode_by_day') ?>" dataUrl="<?= route_to( <Charts.XY title="<?= lang('Charts.episode_by_day') ?>" dataUrl="<?= route_to(
'analytics-filtered-data', 'analytics-filtered-data',
$podcast->id, $podcast->id,
'PodcastByEpisode', 'PodcastByEpisode',
'ByDay', 'ByDay',
$episode->id, $episode->id,
) ?>"/> ) ?>"/>
<Charts.XY title="<?= lang('Charts.episode_by_month') ?>" dataUrl="<?= route_to( <Charts.XY title="<?= lang('Charts.episode_by_month') ?>" dataUrl="<?= route_to(
'analytics-filtered-data', 'analytics-filtered-data',
$podcast->id, $podcast->id,
'PodcastByEpisode', 'PodcastByEpisode',
'ByMonth', 'ByMonth',
$episode->id, $episode->id,
) ?>"/> ) ?>"/>
</div> </div>

View File

@ -58,12 +58,12 @@ $counts = [
<div class="flex flex-col items-start flex-1 w-48 px-2"> <div class="flex flex-col items-start flex-1 w-48 px-2">
<span class="w-full font-semibold truncate" title="<?= esc($podcast->title) ?>"><?= esc($podcast->title) ?></span> <span class="w-full font-semibold truncate" title="<?= esc($podcast->title) ?>"><?= esc($podcast->title) ?></span>
<a href="<?= route_to( <a href="<?= route_to(
'podcast-activity', 'podcast-activity',
esc($podcast->handle), esc($podcast->handle),
) ?>" class="inline-flex items-center text-sm hover:underline focus:ring-accent" ) ?>" class="inline-flex items-center text-sm hover:underline focus:ring-accent"
data-tooltip="bottom" title="<?= lang( data-tooltip="bottom" title="<?= lang(
'PodcastNavigation.go_to_page', 'PodcastNavigation.go_to_page',
) ?>">@<?= esc($podcast->handle) ?> ) ?>">@<?= esc($podcast->handle) ?>
<?= icon('external-link', 'ml-1 opacity-60') ?> <?= icon('external-link', 'ml-1 opacity-60') ?>
</a> </a>
</div> </div>
@ -73,16 +73,16 @@ $counts = [
<div> <div>
<button class="inline-flex items-center w-full px-4 py-1 font-semibold focus:ring-accent" type="button"> <button class="inline-flex items-center w-full px-4 py-1 font-semibold focus:ring-accent" type="button">
<?= icon($data['icon'], 'opacity-60 text-2xl mr-4') . <?= icon($data['icon'], 'opacity-60 text-2xl mr-4') .
lang('PodcastNavigation.' . $section) ?> lang('PodcastNavigation.' . $section) ?>
</button> </button>
<ul class="flex flex-col"> <ul class="flex flex-col">
<?php foreach ($data['items'] as $item): ?> <?php foreach ($data['items'] as $item): ?>
<?php $isActive = url_is(route_to($item, $podcast->id)); ?> <?php $isActive = url_is(route_to($item, $podcast->id)); ?>
<?php <?php
$itemLabel = lang('PodcastNavigation.' . $item); $itemLabel = lang('PodcastNavigation.' . $item);
if (array_key_exists($item, $counts)) { if (array_key_exists($item, $counts)) {
$itemLabel .= ' (' . $counts[$item] . ')'; $itemLabel .= ' (' . $counts[$item] . ')';
} }
?> ?>
<li class="inline-flex"> <li class="inline-flex">
<a class="w-full py-1 pl-14 pr-2 text-sm hover:opacity-100 focus:ring-inset focus:ring-accent <?= $isActive <a class="w-full py-1 pl-14 pr-2 text-sm hover:opacity-100 focus:ring-inset focus:ring-accent <?= $isActive

View File

@ -12,25 +12,25 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.XY class="col-span-1" title="<?= lang('Charts.podcast_by_day') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.podcast_by_day') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'ByDay', 'ByDay',
) ?>"/> ) ?>"/>
<Charts.XY class="col-span-1" title="<?= lang('Charts.podcast_by_month') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.podcast_by_month') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'ByMonth', 'ByMonth',
) ?>"/> ) ?>"/>
<Charts.XY class="col-span-1" title="<?= lang('Charts.podcast_by_bandwidth') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.podcast_by_bandwidth') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'BandwidthByDay', 'BandwidthByDay',
) ?>"/> ) ?>"/>
</div> </div>
<?= service('vite') <?= service('vite')

View File

@ -12,18 +12,18 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.XY class="col-span-1" title="<?= lang('Charts.daily_listening_time') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.daily_listening_time') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'TotalListeningTimeByDay', 'TotalListeningTimeByDay',
) ?>"/> ) ?>"/>
<Charts.XY class="col-span-1" title="<?= lang('Charts.monthly_listening_time') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.monthly_listening_time') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'TotalListeningTimeByMonth', 'TotalListeningTimeByMonth',
) ?>"/> ) ?>"/>
</div> </div>
<?= service('vite') <?= service('vite')

View File

@ -12,22 +12,22 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.Pie title="<?= lang('Charts.by_country_weekly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_country_weekly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByCountry', 'PodcastByCountry',
'Weekly', 'Weekly',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_country_yearly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_country_yearly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByCountry', 'PodcastByCountry',
'Yearly', 'Yearly',
) ?>" /> ) ?>" />
<Charts.Map class="col-span-2" title="<?= lang('Charts.podcast_by_region') ?>" dataUrl="<?= route_to( <Charts.Map class="col-span-2" title="<?= lang('Charts.podcast_by_region') ?>" dataUrl="<?= route_to(
'analytics-full-data', 'analytics-full-data',
$podcast->id, $podcast->id,
'PodcastByRegion', 'PodcastByRegion',
) ?>" /> ) ?>" />
</div> </div>

View File

@ -12,35 +12,35 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.Pie title="<?= lang('Charts.by_player_weekly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_player_weekly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByPlayer', 'PodcastByPlayer',
'ByAppWeekly', 'ByAppWeekly',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_service_weekly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_service_weekly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByService', 'PodcastByService',
'ByServiceWeekly', 'ByServiceWeekly',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_device_weekly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_device_weekly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByPlayer', 'PodcastByPlayer',
'ByDeviceWeekly', 'ByDeviceWeekly',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_os_weekly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_os_weekly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByPlayer', 'PodcastByPlayer',
'ByOsWeekly', 'ByOsWeekly',
) ?>" /> ) ?>" />
<Charts.XY class="col-span-2" title="<?= lang('Charts.podcast_bots') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-2" title="<?= lang('Charts.podcast_bots') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByPlayer', 'PodcastByPlayer',
'Bots', 'Bots',
) ?>" /> ) ?>" />
</div> </div>
<?= service('vite') <?= service('vite')

View File

@ -12,16 +12,16 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.Bar title="<?= lang('Charts.by_weekday') ?>" dataUrl="<?= route_to( <Charts.Bar title="<?= lang('Charts.by_weekday') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'ByWeekday', 'ByWeekday',
) ?>" /> ) ?>" />
<Charts.Bar title="<?= lang('Charts.by_hour') ?>" dataUrl="<?= route_to( <Charts.Bar title="<?= lang('Charts.by_hour') ?>" dataUrl="<?= route_to(
'analytics-full-data', 'analytics-full-data',
$podcast->id, $podcast->id,
'PodcastByHour', 'PodcastByHour',
) ?>" /> ) ?>" />
</div> </div>
<?= service('vite') <?= service('vite')

View File

@ -12,18 +12,18 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.XY class="col-span-1" title="<?= lang('Charts.unique_daily_listeners') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.unique_daily_listeners') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'UniqueListenersByDay', 'UniqueListenersByDay',
) ?>"/> ) ?>"/>
<Charts.XY class="col-span-1" title="<?= lang('Charts.unique_monthly_listeners') ?>" dataUrl="<?= route_to( <Charts.XY class="col-span-1" title="<?= lang('Charts.unique_monthly_listeners') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'Podcast', 'Podcast',
'UniqueListenersByMonth', 'UniqueListenersByMonth',
) ?>"/> ) ?>"/>
</div> </div>
<?= service('vite') <?= service('vite')

View File

@ -12,27 +12,27 @@
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2"> <div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Charts.Pie title="<?= lang('Charts.by_domain_weekly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_domain_weekly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'WebsiteByReferer', 'WebsiteByReferer',
'ByDomainWeekly', 'ByDomainWeekly',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_domain_yearly') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_domain_yearly') ?>" dataUrl="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'WebsiteByReferer', 'WebsiteByReferer',
'ByDomainYearly', 'ByDomainYearly',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_entry_page') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_entry_page') ?>" dataUrl="<?= route_to(
'analytics-full-data', 'analytics-full-data',
$podcast->id, $podcast->id,
'WebsiteByEntryPage', 'WebsiteByEntryPage',
) ?>" /> ) ?>" />
<Charts.Pie title="<?= lang('Charts.by_browser') ?>" dataUrl="<?= route_to( <Charts.Pie title="<?= lang('Charts.by_browser') ?>" dataUrl="<?= route_to(
'analytics-full-data', 'analytics-full-data',
$podcast->id, $podcast->id,
'WebsiteByBrowser', 'WebsiteByBrowser',
) ?>" /> ) ?>" />
</div> </div>

View File

@ -111,11 +111,11 @@
<Forms.RadioButton <Forms.RadioButton
value="clean" value="clean"
name="parental_advisory" name="parental_advisory"
isChecked="false" ><?= lang('Podcast.form.parental_advisory.clean', ) ?></Forms.RadioButton> isChecked="false" ><?= lang('Podcast.form.parental_advisory.clean') ?></Forms.RadioButton>
<Forms.RadioButton <Forms.RadioButton
value="explicit" value="explicit"
name="parental_advisory" name="parental_advisory"
isChecked="false" ><?= lang('Podcast.form.parental_advisory.explicit', ) ?></Forms.RadioButton> isChecked="false" ><?= lang('Podcast.form.parental_advisory.explicit') ?></Forms.RadioButton>
</div> </div>
</fieldset> </fieldset>
</Forms.Section> </Forms.Section>

View File

@ -128,11 +128,11 @@
<Forms.RadioButton <Forms.RadioButton
value="clean" value="clean"
name="parental_advisory" name="parental_advisory"
isChecked="<?= $podcast->parental_advisory === 'clean' ? 'true' : 'false' ?>" ><?= lang('Podcast.form.parental_advisory.clean', ) ?></Forms.RadioButton> isChecked="<?= $podcast->parental_advisory === 'clean' ? 'true' : 'false' ?>" ><?= lang('Podcast.form.parental_advisory.clean') ?></Forms.RadioButton>
<Forms.RadioButton <Forms.RadioButton
value="explicit" value="explicit"
name="parental_advisory" name="parental_advisory"
isChecked="<?= $podcast->parental_advisory === 'explicit' ? 'true' : 'false' ?>" ><?= lang('Podcast.form.parental_advisory.explicit', ) ?></Forms.RadioButton> isChecked="<?= $podcast->parental_advisory === 'explicit' ? 'true' : 'false' ?>" ><?= lang('Podcast.form.parental_advisory.explicit') ?></Forms.RadioButton>
</div> </div>
</fieldset> </fieldset>
</Forms.Section> </Forms.Section>

View File

@ -2,9 +2,9 @@
<header class="flex justify-between"> <header class="flex justify-between">
<Heading tagName="h2"><?= lang('Podcast.latest_episodes') ?></Heading> <Heading tagName="h2"><?= lang('Podcast.latest_episodes') ?></Heading>
<a href="<?= route_to( <a href="<?= route_to(
'episode-list', 'episode-list',
$podcast->id, $podcast->id,
) ?>" class="inline-flex items-center text-sm underline hover:no-underline focus:ring-accent"> ) ?>" class="inline-flex items-center text-sm underline hover:no-underline focus:ring-accent">
<?= lang('Podcast.see_all_episodes') ?> <?= lang('Podcast.see_all_episodes') ?>
<?= icon('chevron-right', 'ml-2') ?> <?= icon('chevron-right', 'ml-2') ?>
</a> </a>
@ -13,8 +13,8 @@
<div class="grid px-4 pt-2 pb-5 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity"> <div class="grid px-4 pt-2 pb-5 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity">
<?php foreach ($episodes as $episode): ?> <?php foreach ($episodes as $episode): ?>
<?= view('episode/_card', [ <?= view('episode/_card', [
'episode' => $episode, 'episode' => $episode,
]) ?> ]) ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php else: ?> <?php else: ?>

View File

@ -19,47 +19,47 @@
<div class="-mx-2 -mt-8 border-b divide-y md:-mx-12"> <div class="-mx-2 -mt-8 border-b divide-y md:-mx-12">
<?php <?php
foreach ($notifications as $notification): foreach ($notifications as $notification):
$backgroundColor = $notification->read_at === null ? 'bg-heading-background' : 'bg-base'; $backgroundColor = $notification->read_at === null ? 'bg-heading-background' : 'bg-base';
?> ?>
<div class="py-3 hover:bg-white px-4 <?= $backgroundColor ?> group"> <div class="py-3 hover:bg-white px-4 <?= $backgroundColor ?> group">
<?php <?php
$post = $notification->post_id !== null ? $notification->post : null; $post = $notification->post_id !== null ? $notification->post : null;
$actorUsername = '@' . esc($notification->actor $actorUsername = '@' . esc($notification->actor
->username) . ->username) .
($notification->actor->is_local ($notification->actor->is_local
? '' ? ''
: '@' . esc($notification->actor->domain)); : '@' . esc($notification->actor->domain));
$actorUsernameHtml = <<<CODE_SAMPLE $actorUsernameHtml = <<<CODE_SAMPLE
<strong class="break-all">{$actorUsername}</strong> <strong class="break-all">{$actorUsername}</strong>
CODE_SAMPLE; CODE_SAMPLE;
$targetActorUsername = '@' . esc($notification->target_actor->username); $targetActorUsername = '@' . esc($notification->target_actor->username);
$targetActorUsernameHtml = <<<CODE_SAMPLE $targetActorUsernameHtml = <<<CODE_SAMPLE
<strong class="break-all">{$targetActorUsername}</strong> <strong class="break-all">{$targetActorUsername}</strong>
CODE_SAMPLE; CODE_SAMPLE;
$notificationTitle = match ($notification->type) { $notificationTitle = match ($notification->type) {
'reply' => lang('Notifications.reply', [ 'reply' => lang('Notifications.reply', [
'actor_username' => $actorUsernameHtml, 'actor_username' => $actorUsernameHtml,
], null, false), ], null, false),
'like' => lang('Notifications.favourite', [ 'like' => lang('Notifications.favourite', [
'actor_username' => $actorUsernameHtml, 'actor_username' => $actorUsernameHtml,
], null, false), ], null, false),
'share' => lang('Notifications.reblog', [ 'share' => lang('Notifications.reblog', [
'actor_username' => $actorUsernameHtml, 'actor_username' => $actorUsernameHtml,
], null, false), ], null, false),
'follow' => lang('Notifications.follow', [ 'follow' => lang('Notifications.follow', [
'actor_username' => $actorUsernameHtml, 'actor_username' => $actorUsernameHtml,
], null, false), ], null, false),
default => '', default => '',
}; };
$notificationContent = $post !== null ? $post->message_html : null; $notificationContent = $post !== null ? $post->message_html : null;
$postLink = $post !== null ? route_to('post', esc($podcast->handle), $post->id) : route_to('podcast-activity', esc($podcast->handle)); $postLink = $post !== null ? route_to('post', esc($podcast->handle), $post->id) : route_to('podcast-activity', esc($podcast->handle));
$link = $notification->read_at !== null ? $postLink : route_to('notification-mark-as-read', $podcast->id, $notification->id); $link = $notification->read_at !== null ? $postLink : route_to('notification-mark-as-read', $podcast->id, $notification->id);
?> ?>
<a href="<?= $link ?>"> <a href="<?= $link ?>">
<div class="flex items-start md:items-center"> <div class="flex items-start md:items-center">
@ -76,7 +76,7 @@
'follow' => icon('user-follow', 'text-violet-500 text-base'), 'follow' => icon('user-follow', 'text-violet-500 text-base'),
default => '', default => '',
}; };
?> ?>
<?= $icon ?> <?= $icon ?>
</span> </span>
</div> </div>

View File

@ -23,28 +23,28 @@
<div class="flex flex-col items-center w-12 mr-4"> <div class="flex flex-col items-center w-12 mr-4">
<?php if ($platform->submit_url === ''): ?> <?php if ($platform->submit_url === ''): ?>
<?= icon( <?= icon(
esc($platform->slug), esc($platform->slug),
'text-skin-muted text-4xl', 'text-skin-muted text-4xl',
$platform->type $platform->type
) ?> ) ?>
<?php else: ?> <?php else: ?>
<?= anchor( <?= anchor(
$platform->submit_url, $platform->submit_url,
icon( icon(
esc($platform->slug), esc($platform->slug),
'text-skin-muted text-4xl', 'text-skin-muted text-4xl',
$platform->type $platform->type
), ),
[ [
'class' => 'text-skin-muted hover:text-skin-base', 'class' => 'text-skin-muted hover:text-skin-base',
'target' => '_blank', 'target' => '_blank',
'rel' => 'noopener noreferrer', 'rel' => 'noopener noreferrer',
'data-tooltip' => 'bottom', 'data-tooltip' => 'bottom',
'title' => lang('Platforms.submit_url', [ 'title' => lang('Platforms.submit_url', [
'platformName' => $platform->label, 'platformName' => $platform->label,
]), ]),
], ],
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<div class="inline-flex mt-1 bg-highlight"> <div class="inline-flex mt-1 bg-highlight">
<?= anchor($platform->home_url, icon('external-link', 'mx-auto'), [ <?= anchor($platform->home_url, icon('external-link', 'mx-auto'), [
@ -57,37 +57,37 @@
]), ]),
]) ?> ]) ?>
<?= $platform->submit_url <?= $platform->submit_url
? anchor($platform->submit_url, icon('add', 'mx-auto'), [ ? anchor($platform->submit_url, icon('add', 'mx-auto'), [
'class' => 'flex-1 text-skin-muted hover:text-skin-base', 'class' => 'flex-1 text-skin-muted hover:text-skin-base',
'target' => '_blank', 'target' => '_blank',
'rel' => 'noopener noreferrer', 'rel' => 'noopener noreferrer',
'data-tooltip' => 'bottom', 'data-tooltip' => 'bottom',
'title' => lang('Platforms.submit_url', [ 'title' => lang('Platforms.submit_url', [
'platformName' => $platform->label, 'platformName' => $platform->label,
]), ]),
]) ])
: '' ?> : '' ?>
</div> </div>
</div> </div>
<div class="flex flex-col flex-1"> <div class="flex flex-col flex-1">
<?= $platform->link_url <?= $platform->link_url
? anchor( ? anchor(
route_to( route_to(
'podcast-platform-remove', 'podcast-platform-remove',
$podcast->id, $podcast->id,
esc($platform->slug), esc($platform->slug),
), ),
icon('delete-bin', 'mx-auto'), icon('delete-bin', 'mx-auto'),
[ [
'class' => 'class' =>
'absolute right-0 p-1 bg-red-100 rounded-full text-red-700 hover:text-red-900', 'absolute right-0 p-1 bg-red-100 rounded-full text-red-700 hover:text-red-900',
'data-tooltip' => 'bottom', 'data-tooltip' => 'bottom',
'title' => lang('Platforms.remove', [ 'title' => lang('Platforms.remove', [
'platformName' => $platform->label, 'platformName' => $platform->label,
]), ]),
], ],
) )
: '' ?> : '' ?>
<fieldset> <fieldset>
<legend class="mb-2 font-semibold"><?= $platform->label ?></legend> <legend class="mb-2 font-semibold"><?= $platform->label ?></legend>
<Forms.Input <Forms.Input

View File

@ -48,8 +48,8 @@
<fieldset class="flex flex-col"> <fieldset class="flex flex-col">
<legend class="text-lg font-semibold"><?= lang( <legend class="text-lg font-semibold"><?= lang(
'Podcast.publish_form.publication_date', 'Podcast.publish_form.publication_date',
) ?></legend> ) ?></legend>
<Forms.Radio value="now" name="publication_method" isChecked="<?= old('publication_method') ? old('publish') === 'now' : true ?>"><?= lang('Podcast.publish_form.publication_method.now') ?></Forms.Radio> <Forms.Radio value="now" name="publication_method" isChecked="<?= old('publication_method') ? old('publish') === 'now' : true ?>"><?= lang('Podcast.publish_form.publication_method.now') ?></Forms.Radio>
<div class="inline-flex flex-wrap items-center radio-toggler"> <div class="inline-flex flex-wrap items-center radio-toggler">
<input <input

View File

@ -3,7 +3,7 @@
<?php if ($subscription->expires_at): ?> <?php if ($subscription->expires_at): ?>
<?php <?php
$formatter = new IntlDateFormatter($subscription->podcast->language_code, IntlDateFormatter::LONG, IntlDateFormatter::LONG); $formatter = new IntlDateFormatter($subscription->podcast->language_code, IntlDateFormatter::LONG, IntlDateFormatter::LONG);
$translatedDate = $subscription->expires_at->toLocalizedString($formatter->getPattern()); $translatedDate = $subscription->expires_at->toLocalizedString($formatter->getPattern());
?> ?>
<?= lang('Subscription.emails.edited_expires', [ <?= lang('Subscription.emails.edited_expires', [
'podcastTitle' => '<strong>' . $subscription->podcast->title . '</strong>', 'podcastTitle' => '<strong>' . $subscription->podcast->title . '</strong>',

View File

@ -11,7 +11,7 @@
<?php if ($subscription->expires_at): ?> <?php if ($subscription->expires_at): ?>
<?php <?php
$formatter = new IntlDateFormatter($subscription->podcast->language_code, IntlDateFormatter::LONG, IntlDateFormatter::LONG); $formatter = new IntlDateFormatter($subscription->podcast->language_code, IntlDateFormatter::LONG, IntlDateFormatter::LONG);
$translatedDate = $subscription->expires_at->toLocalizedString($formatter->getPattern()); $translatedDate = $subscription->expires_at->toLocalizedString($formatter->getPattern());
?> ?>
<?= lang('Subscription.emails.welcome_expires', ['<strong>' . $translatedDate . '</strong>'], $subscription->podcast->language_code, false) ?> <?= lang('Subscription.emails.welcome_expires', ['<strong>' . $translatedDate . '</strong>'], $subscription->podcast->language_code, false) ?>
<?php else: ?> <?php else: ?>

View File

@ -32,99 +32,99 @@
<hr class="my-6 border-subtle"> <hr class="my-6 border-subtle">
<?= data_table( <?= data_table(
[ [
[ [
'header' => lang('Subscription.list.number'), 'header' => lang('Subscription.list.number'),
'cell' => function ($subscription) { 'cell' => function ($subscription) {
return '#' . $subscription->id; return '#' . $subscription->id;
}, },
], ],
[ [
'header' => lang('Subscription.list.email'), 'header' => lang('Subscription.list.email'),
'cell' => function ($subscription) { 'cell' => function ($subscription) {
return esc($subscription->email); return esc($subscription->email);
}, },
], ],
[ [
'header' => lang('Subscription.list.expiration_date'), 'header' => lang('Subscription.list.expiration_date'),
'cell' => function ($subscription) { 'cell' => function ($subscription) {
return $subscription->expires_at ? local_date($subscription->expires_at) : lang('Subscription.list.unlimited'); return $subscription->expires_at ? local_date($subscription->expires_at) : lang('Subscription.list.unlimited');
}, },
], ],
[ [
'header' => lang('Subscription.list.downloads'), 'header' => lang('Subscription.list.downloads'),
'cell' => function ($subscription) { 'cell' => function ($subscription) {
return $subscription->downloads_last_3_months; return $subscription->downloads_last_3_months;
}, },
], ],
[ [
'header' => lang('Subscription.list.status'), 'header' => lang('Subscription.list.status'),
'cell' => function ($subscription) { 'cell' => function ($subscription) {
$statusMapping = [ $statusMapping = [
'active' => 'success', 'active' => 'success',
'suspended' => 'warning', 'suspended' => 'warning',
'expired' => 'default', 'expired' => 'default',
]; ];
return '<Pill variant="' . $statusMapping[$subscription->status] . '" class="lowercase">' . lang('Subscription.status.' . $subscription->status) . '</Pill>'; return '<Pill variant="' . $statusMapping[$subscription->status] . '" class="lowercase">' . lang('Subscription.status.' . $subscription->status) . '</Pill>';
}, },
], ],
[ [
'header' => lang('Common.actions'), 'header' => lang('Common.actions'),
'cell' => function ($subscription, $podcast) { 'cell' => function ($subscription, $podcast) {
$items = [ $items = [
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Subscription.view'), 'title' => lang('Subscription.view'),
'uri' => route_to('subscription-view', $podcast->id, $subscription->id), 'uri' => route_to('subscription-view', $podcast->id, $subscription->id),
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Subscription.edit'), 'title' => lang('Subscription.edit'),
'uri' => route_to('subscription-edit', $podcast->id, $subscription->id), 'uri' => route_to('subscription-edit', $podcast->id, $subscription->id),
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Subscription.regenerate_token'), 'title' => lang('Subscription.regenerate_token'),
'uri' => route_to('subscription-regenerate-token', $podcast->id, $subscription->id), 'uri' => route_to('subscription-regenerate-token', $podcast->id, $subscription->id),
], ],
[ [
'type' => 'separator', 'type' => 'separator',
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Subscription.delete'), 'title' => lang('Subscription.delete'),
'uri' => route_to('subscription-delete', $podcast->id, $subscription->id), 'uri' => route_to('subscription-delete', $podcast->id, $subscription->id),
'class' => 'font-semibold text-red-600', 'class' => 'font-semibold text-red-600',
], ],
]; ];
if ($subscription->status === 'suspended') { if ($subscription->status === 'suspended') {
$suspendAction = [[ $suspendAction = [[
'type' => 'link', 'type' => 'link',
'title' => lang('Subscription.resume'), 'title' => lang('Subscription.resume'),
'uri' => route_to('subscription-resume', $podcast->id, $subscription->id), 'uri' => route_to('subscription-resume', $podcast->id, $subscription->id),
]]; ]];
} else { } else {
$suspendAction = [[ $suspendAction = [[
'type' => 'link', 'type' => 'link',
'title' => lang('Subscription.suspend'), 'title' => lang('Subscription.suspend'),
'uri' => route_to('subscription-suspend', $podcast->id, $subscription->id), 'uri' => route_to('subscription-suspend', $podcast->id, $subscription->id),
]]; ]];
} }
array_splice($items, 3, 0, $suspendAction); array_splice($items, 3, 0, $suspendAction);
return '<button id="more-dropdown-' . $subscription->id . '" type="button" class="inline-flex items-center p-1 rounded-full focus:ring-accent" data-dropdown="button" data-dropdown-target="more-dropdown-' . $subscription->id . '-menu" aria-haspopup="true" aria-expanded="false">' . return '<button id="more-dropdown-' . $subscription->id . '" type="button" class="inline-flex items-center p-1 rounded-full focus:ring-accent" data-dropdown="button" data-dropdown-target="more-dropdown-' . $subscription->id . '-menu" aria-haspopup="true" aria-expanded="false">' .
icon('more') . icon('more') .
'</button>' . '</button>' .
'<DropdownMenu id="more-dropdown-' . $subscription->id . '-menu" labelledby="more-dropdown-' . $subscription->id . '" offsetY="-24" items="' . esc(json_encode($items)) . '" />'; '<DropdownMenu id="more-dropdown-' . $subscription->id . '-menu" labelledby="more-dropdown-' . $subscription->id . '" offsetY="-24" items="' . esc(json_encode($items)) . '" />';
}, },
], ],
], ],
$podcast->subscriptions, $podcast->subscriptions,
'', '',
$podcast, $podcast,
) ?> ) ?>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@ -7,7 +7,7 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<a href="<?= route_to('home') ?>" class="inline-flex items-center h-full px-2 border-r border-navigation focus:ring-inset focus:ring-accent"> <a href="<?= route_to('home') ?>" class="inline-flex items-center h-full px-2 border-r border-navigation focus:ring-inset focus:ring-accent">
<?= svg('castopod-logo-base', 'h-6') ?> <?= svg('castopod-logo-base', 'h-6') ?>
</a> </a>
<a href="<?= route_to('admin', ) ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold hover:underline focus:ring-inset focus:ring-accent"> <a href="<?= route_to('admin') ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold hover:underline focus:ring-inset focus:ring-accent">
<?= lang('Navigation.go_to_admin') ?> <?= lang('Navigation.go_to_admin') ?>
<?= icon('external-link', 'ml-1 opacity-60') ?> <?= icon('external-link', 'ml-1 opacity-60') ?>
</a> </a>
@ -23,24 +23,24 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<?php <?php
$notificationsTitle = lang('Notifications.title'); $notificationsTitle = lang('Notifications.title');
$items = [ $items = [
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span> <span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span>
CODE_SAMPLE), CODE_SAMPLE),
], ],
]; ];
if ($userPodcasts !== []) { if ($userPodcasts !== []) {
foreach ($userPodcasts as $userPodcast) { foreach ($userPodcasts as $userPodcast) {
$userPodcastTitle = esc($userPodcast->title); $userPodcastTitle = esc($userPodcast->title);
$unreadNotificationDotDisplayClass = in_array($userPodcast->actor_id, $actorIdsWithUnreadNotifications, true) ? '' : 'hidden'; $unreadNotificationDotDisplayClass = in_array($userPodcast->actor_id, $actorIdsWithUnreadNotifications, true) ? '' : 'hidden';
$items[] = [ $items[] = [
'type' => 'link', 'type' => 'link',
'title' => <<<CODE_SAMPLE 'title' => <<<CODE_SAMPLE
<div class="inline-flex items-center flex-1 text-sm align-middle"> <div class="inline-flex items-center flex-1 text-sm align-middle">
<div class="relative"> <div class="relative">
<img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /> <img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" />
@ -49,20 +49,20 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<span class="max-w-xs truncate">{$userPodcastTitle}</span> <span class="max-w-xs truncate">{$userPodcastTitle}</span>
</div> </div>
CODE_SAMPLE CODE_SAMPLE
, ,
'uri' => route_to('notification-list', $userPodcast->id), 'uri' => route_to('notification-list', $userPodcast->id),
]; ];
} }
} else { } else {
$noNotificationsText = lang('Notifications.no_notifications'); $noNotificationsText = lang('Notifications.no_notifications');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span> <span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span>
CODE_SAMPLE), CODE_SAMPLE),
]; ];
} }
?> ?>
<DropdownMenu id="notifications-dropdown-menu" labelledby="notifications-dropdown" items="<?= esc(json_encode($items)) ?>" placement="bottom"/> <DropdownMenu id="notifications-dropdown-menu" labelledby="notifications-dropdown" items="<?= esc(json_encode($items)) ?>" placement="bottom"/>
<button <button
@ -84,50 +84,50 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
<?= esc(auth()->user()->username) ?> <?= esc(auth()->user()->username) ?>
<?= icon('caret-down', 'ml-auto text-2xl') ?></button> <?= icon('caret-down', 'ml-auto text-2xl') ?></button>
<?php <?php
$interactButtons = ''; $interactButtons = '';
foreach ($userPodcasts as $userPodcast) { foreach ($userPodcasts as $userPodcast) {
if (can_podcast(auth()->user(), $userPodcast->id, 'interact-as')) { if (can_podcast(auth()->user(), $userPodcast->id, 'interact-as')) {
$checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : ''; $checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : '';
$userPodcastTitle = esc($userPodcast->title); $userPodcastTitle = esc($userPodcast->title);
$interactButtons .= <<<CODE_SAMPLE $interactButtons .= <<<CODE_SAMPLE
<button class="inline-flex items-center w-full px-4 py-1 hover:bg-highlight" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}"> <button class="inline-flex items-center w-full px-4 py-1 hover:bg-highlight" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}">
<div class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /><span class="max-w-xs truncate">{$userPodcastTitle}</span>{$checkMark}</div> <div class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /><span class="max-w-xs truncate">{$userPodcastTitle}</span>{$checkMark}</div>
</button> </button>
CODE_SAMPLE; CODE_SAMPLE;
} }
} }
$interactAsText = lang('Common.choose_interact'); $interactAsText = lang('Common.choose_interact');
$interactAsRoute = route_to('interact-as-actor'); $interactAsRoute = route_to('interact-as-actor');
$csrfField = csrf_field(); $csrfField = csrf_field();
$menuItems = [ $menuItems = [
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Navigation.account.my-account'), 'title' => lang('Navigation.account.my-account'),
'uri' => route_to('my-account'), 'uri' => route_to('my-account'),
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Navigation.account.change-password'), 'title' => lang('Navigation.account.change-password'),
'uri' => route_to('change-password'), 'uri' => route_to('change-password'),
], ],
[ [
'type' => 'separator', 'type' => 'separator',
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => lang('Navigation.account.logout'), 'title' => lang('Navigation.account.logout'),
'uri' => route_to('logout'), 'uri' => route_to('logout'),
], ],
]; ];
if ($userPodcasts !== []) { if ($userPodcasts !== []) {
$menuItems = array_merge([ $menuItems = array_merge([
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<CODE_SAMPLE
<nav class="flex flex-col py-2 whitespace-nowrap"> <nav class="flex flex-col py-2 whitespace-nowrap">
<span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span> <span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span>
<form action="{$interactAsRoute}" method="POST" class="flex flex-col"> <form action="{$interactAsRoute}" method="POST" class="flex flex-col">
@ -136,13 +136,13 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
</form> </form>
</nav> </nav>
CODE_SAMPLE), CODE_SAMPLE),
], ],
[ [
'type' => 'separator', 'type' => 'separator',
], ],
], $menuItems); ], $menuItems);
} }
?> ?>
<DropdownMenu id="my-account-dropdown-menu" labelledby="my-account-dropdown" items="<?= esc(json_encode($menuItems)) ?>" /> <DropdownMenu id="my-account-dropdown-menu" labelledby="my-account-dropdown" items="<?= esc(json_encode($menuItems)) ?>" />
</div> </div>
</div> </div>

View File

@ -27,17 +27,17 @@
<?php endif; ?> <?php endif; ?>
</h4> </h4>
<p class="text-xs text-skin-muted"><?= implode( <p class="text-xs text-skin-muted"><?= implode(
', ', ', ',
array_map(function ($role) { array_map(function ($role) {
return lang( return lang(
'PersonsTaxonomy.persons.' . 'PersonsTaxonomy.persons.' .
$role->group . $role->group .
'.roles.' . '.roles.' .
$role->role . $role->role .
'.label', '.label',
); );
}, $person->roles), }, $person->roles),
) ?></p> ) ?></p>
</div> </div>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>

View File

@ -63,18 +63,18 @@
<button class="p-2 text-red-600 bg-white rounded-full shadow hover:text-red-500 focus:ring-accent" data-toggle="funding-links" data-toggle-class="hidden" title="<?= lang('Podcast.sponsor') ?>"><Icon glyph="heart"></Icon></button> <button class="p-2 text-red-600 bg-white rounded-full shadow hover:text-red-500 focus:ring-accent" data-toggle="funding-links" data-toggle-class="hidden" title="<?= lang('Podcast.sponsor') ?>"><Icon glyph="heart"></Icon></button>
<?php endif; ?> <?php endif; ?>
<?= anchor_popup( <?= anchor_popup(
route_to('follow', esc($podcast->handle)), route_to('follow', esc($podcast->handle)),
icon( icon(
'social/castopod', 'social/castopod',
'mr-2 text-xl text-black/75 group-hover:text-black', 'mr-2 text-xl text-black/75 group-hover:text-black',
) . lang('Podcast.follow'), ) . lang('Podcast.follow'),
[ [
'width' => 420, 'width' => 420,
'height' => 620, 'height' => 620,
'class' => 'class' =>
'group inline-flex items-center px-3 leading-8 text-xs tracking-wider font-semibold text-black uppercase rounded-full shadow focus:ring-accent bg-white', 'group inline-flex items-center px-3 leading-8 text-xs tracking-wider font-semibold text-black uppercase rounded-full shadow focus:ring-accent bg-white',
], ],
) ?> ) ?>
</div> </div>
</nav> </nav>
<header class="relative z-50 flex flex-col col-start-2 px-8 pt-8 pb-4 overflow-hidden bg-accent-base/75 gap-y-4"> <header class="relative z-50 flex flex-col col-start-2 px-8 pt-8 pb-4 overflow-hidden bg-accent-base/75 gap-y-4">
@ -98,9 +98,10 @@
<?php $i = 0; ?> <?php $i = 0; ?>
<?php foreach ($episode->persons as $person): ?> <?php foreach ($episode->persons as $person): ?>
<img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= esc($person->full_name) ?>" class="object-cover w-8 h-8 -ml-4 border-2 rounded-full aspect-square border-background-header last:ml-0" loading="lazy" /> <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= esc($person->full_name) ?>" class="object-cover w-8 h-8 -ml-4 border-2 rounded-full aspect-square border-background-header last:ml-0" loading="lazy" />
<?php $i++; if ($i === 3) { <?php $i++;
break; if ($i === 3) {
}?> break;
}?>
<?php endforeach; ?> <?php endforeach; ?>
</span> </span>
<?= lang('Episode.persons', [ <?= lang('Episode.persons', [

View File

@ -3,41 +3,41 @@
<form action="<?= route_to('episode-comment-attempt-like', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id) ?>" method="POST" class="flex items-center gap-x-4"> <form action="<?= route_to('episode-comment-attempt-like', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id) ?>" method="POST" class="flex items-center gap-x-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang( <button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button> ) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button>
<Button uri="<?= route_to('episode-comment', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id) ?>" size="small"><?= lang('Comment.reply') ?></Button> <Button uri="<?= route_to('episode-comment', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id) ?>" size="small"><?= lang('Comment.reply') ?></Button>
</form> </form>
<?php if ($comment->replies_count): ?> <?php if ($comment->replies_count): ?>
<?= anchor( <?= anchor(
route_to('episode-comment', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id), route_to('episode-comment', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id),
icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [ icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [
'numberOfReplies' => $comment->replies_count, 'numberOfReplies' => $comment->replies_count,
]), ]),
[ [
'class' => 'inline-flex items-center text-xs hover:underline', 'class' => 'inline-flex items-center text-xs hover:underline',
] ]
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php else: ?> <?php else: ?>
<button class="inline-flex items-center opacity-50 cursor-not-allowed hover:underline" title="<?= lang( <button class="inline-flex items-center opacity-50 cursor-not-allowed hover:underline" title="<?= lang(
'Comment.like', 'Comment.like',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?>"><?= icon('heart', 'text-xl mr-1 text-skin-muted') . $comment->likes_count ?></button> ) ?>"><?= icon('heart', 'text-xl mr-1 text-skin-muted') . $comment->likes_count ?></button>
<?php if ($comment->replies_count): ?> <?php if ($comment->replies_count): ?>
<?= anchor( <?= anchor(
route_to('episode-comment', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id), route_to('episode-comment', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id),
icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [ icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [
'numberOfReplies' => $comment->replies_count, 'numberOfReplies' => $comment->replies_count,
]), ]),
[ [
'class' => 'inline-flex items-center text-xs hover:underline', 'class' => 'inline-flex items-center text-xs hover:underline',
] ]
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
</footer> </footer>

View File

@ -3,47 +3,47 @@
<form action="<?= route_to('post-attempt-action', esc(interact_as_actor()->username), $comment->id) ?>" method="POST" class="flex items-center gap-x-4"> <form action="<?= route_to('post-attempt-action', esc(interact_as_actor()->username), $comment->id) ?>" method="POST" class="flex items-center gap-x-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" name="action" value="favourite" class="inline-flex items-center hover:underline group" title="<?= lang( <button type="submit" name="action" value="favourite" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button> ) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button>
<Button uri="<?= route_to('post', esc($podcast->handle), $comment->id) ?>" size="small"><?= lang('Comment.reply') ?></Button> <Button uri="<?= route_to('post', esc($podcast->handle), $comment->id) ?>" size="small"><?= lang('Comment.reply') ?></Button>
</form> </form>
<?php if ($comment->replies_count): ?> <?php if ($comment->replies_count): ?>
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $comment->id), route_to('post', esc($podcast->handle), $comment->id),
icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [ icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [
'numberOfReplies' => $comment->replies_count, 'numberOfReplies' => $comment->replies_count,
]), ]),
[ [
'class' => 'inline-flex items-center text-xs hover:underline', 'class' => 'inline-flex items-center text-xs hover:underline',
] ]
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php else: ?> <?php else: ?>
<?= anchor_popup( <?= anchor_popup(
route_to('post-remote-action', esc($podcast->handle), $comment->id, 'favourite'), route_to('post-remote-action', esc($podcast->handle), $comment->id, 'favourite'),
icon('heart', 'text-xl mr-1 opacity-40') . $comment->likes_count, icon('heart', 'text-xl mr-1 opacity-40') . $comment->likes_count,
[ [
'class' => 'inline-flex items-center hover:underline', 'class' => 'inline-flex items-center hover:underline',
'width' => 420, 'width' => 420,
'height' => 620, 'height' => 620,
'title' => lang('Post.favourites', [ 'title' => lang('Post.favourites', [
'numberOfFavourites' => $comment->likes_count, 'numberOfFavourites' => $comment->likes_count,
]), ]),
], ],
) ?> ) ?>
<?php if ($comment->replies_count): ?> <?php if ($comment->replies_count): ?>
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $comment->id), route_to('post', esc($podcast->handle), $comment->id),
icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [ icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', [
'numberOfReplies' => $comment->replies_count, 'numberOfReplies' => $comment->replies_count,
]), ]),
[ [
'class' => 'inline-flex items-center text-xs hover:underline', 'class' => 'inline-flex items-center text-xs hover:underline',
] ]
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
</footer> </footer>

View File

@ -25,29 +25,29 @@
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang( <button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . lang( ) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?></button> ) ?></button>
</form> </form>
<?php else: ?> <?php else: ?>
<button class="inline-flex items-center opacity-50 cursor-not-allowed" title="<?= lang( <button class="inline-flex items-center opacity-50 cursor-not-allowed" title="<?= lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?>"><?= icon('heart', 'text-xl mr-1 text-skin-muted') . lang( ) ?>"><?= icon('heart', 'text-xl mr-1 text-skin-muted') . lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $comment->likes_count, 'numberOfLikes' => $comment->likes_count,
], ],
) ?></button> ) ?></button>
<?php endif; ?> <?php endif; ?>
</footer> </footer>
<?php endif; ?> <?php endif; ?>

View File

@ -4,31 +4,31 @@
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang( <button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $reply->likes_count, 'numberOfLikes' => $reply->likes_count,
], ],
) ?>"><?= icon('heart', 'text-lg mr-1 text-gray-400 group-hover:text-red-600') . $reply->likes_count ?></button> ) ?>"><?= icon('heart', 'text-lg mr-1 text-gray-400 group-hover:text-red-600') . $reply->likes_count ?></button>
<Button uri="<?= route_to('episode-comment', esc($reply->episode->podcast->handle), esc($reply->episode->slug), $reply->id) ?>" size="small"><?= lang('Comment.reply') ?></Button> <Button uri="<?= route_to('episode-comment', esc($reply->episode->podcast->handle), esc($reply->episode->slug), $reply->id) ?>" size="small"><?= lang('Comment.reply') ?></Button>
</form> </form>
<?php else: ?> <?php else: ?>
<button type="submit" name="action" class="inline-flex items-center opacity-50 cursor-not-allowed" disabled="disabled" title="<?= lang( <button type="submit" name="action" class="inline-flex items-center opacity-50 cursor-not-allowed" disabled="disabled" title="<?= lang(
'Comment.likes', 'Comment.likes',
[ [
'numberOfLikes' => $reply->likes_count, 'numberOfLikes' => $reply->likes_count,
], ],
) ?>"><?= icon('heart', 'text-lg mr-1 text-skin-muted') . $reply->likes_count ?></button> ) ?>"><?= icon('heart', 'text-lg mr-1 text-skin-muted') . $reply->likes_count ?></button>
<?php if ($reply->replies_count): ?> <?php if ($reply->replies_count): ?>
<?= anchor( <?= anchor(
route_to('episode-comment', esc($reply->episode->podcast->handle), esc($reply->episode->slug), $reply->id), route_to('episode-comment', esc($reply->episode->podcast->handle), esc($reply->episode->slug), $reply->id),
icon('chat', 'text-2xl mr-1 opacity-40') . $reply->replies_count, icon('chat', 'text-2xl mr-1 opacity-40') . $reply->replies_count,
[ [
'class' => 'inline-flex items-center hover:underline', 'class' => 'inline-flex items-center hover:underline',
'title' => lang('Comment.replies', [ 'title' => lang('Comment.replies', [
'numberOfReplies' => $reply->replies_count, 'numberOfReplies' => $reply->replies_count,
]), ]),
], ],
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
</footer> </footer>

View File

@ -5,9 +5,9 @@
<nav class="mb-2"> <nav class="mb-2">
<a href="<?= route_to('episode', esc($podcast->handle), esc($episode->slug)) ?>" <a href="<?= route_to('episode', esc($podcast->handle), esc($episode->slug)) ?>"
class="inline-flex items-center px-4 py-2 text-sm focus:ring-accent"><?= icon( class="inline-flex items-center px-4 py-2 text-sm focus:ring-accent"><?= icon(
'arrow-left', 'arrow-left',
'mr-2 text-lg', 'mr-2 text-lg',
) . lang('Comment.back_to_comments') ?></a> ) . lang('Comment.back_to_comments') ?></a>
</nav> </nav>
<div class="pb-12"> <div class="pb-12">
<?= $this->include('episode/_partials/comment_with_replies') ?> <?= $this->include('episode/_partials/comment_with_replies') ?>

View File

@ -39,8 +39,8 @@
<header class="py-8 text-white border-b bg-header border-subtle"> <header class="py-8 text-white border-b bg-header border-subtle">
<h1 class="container flex items-center justify-between px-2 py-4 mx-auto"> <h1 class="container flex items-center justify-between px-2 py-4 mx-auto">
<a href="<?= route_to( <a href="<?= route_to(
'home', 'home',
) ?>" class="inline-flex items-baseline text-3xl font-semibold font-display"><?= service('settings') ) ?>" class="inline-flex items-baseline text-3xl font-semibold font-display"><?= service('settings')
->get('App.siteName') === 'Castopod' ? 'castopod' . ->get('App.siteName') === 'Castopod' ? 'castopod' .
svg('castopod-logo-base', 'h-6 ml-2') : esc(service('settings') svg('castopod-logo-base', 'h-6 ml-2') : esc(service('settings')
->get('App.siteName')) ?></a> ->get('App.siteName')) ?></a>
@ -49,29 +49,29 @@
<main class="container flex-1 px-4 py-10 mx-auto"> <main class="container flex-1 px-4 py-10 mx-auto">
<div class="flex flex-wrap items-center justify-between py-2 border-b border-subtle gap-x-4"> <div class="flex flex-wrap items-center justify-between py-2 border-b border-subtle gap-x-4">
<Heading tagName="h2" class="inline-block"><?= lang('Home.all_podcasts') ?> (<?= count( <Heading tagName="h2" class="inline-block"><?= lang('Home.all_podcasts') ?> (<?= count(
$podcasts, $podcasts,
) ?>)</Heading> ) ?>)</Heading>
<button class="inline-flex items-center px-2 py-1 text-sm font-semibold focus:ring-accent" id="sortby-dropdown" data-dropdown="button" data-dropdown-target="sortby-dropdown-menu" aria-haspopup="true" aria-expanded="false"><?= icon('sort', 'mr-1 text-xl opacity-50') . lang('Home.sort_by') ?></button> <button class="inline-flex items-center px-2 py-1 text-sm font-semibold focus:ring-accent" id="sortby-dropdown" data-dropdown="button" data-dropdown-target="sortby-dropdown-menu" aria-haspopup="true" aria-expanded="false"><?= icon('sort', 'mr-1 text-xl opacity-50') . lang('Home.sort_by') ?></button>
<DropdownMenu id="sortby-dropdown-menu" labelledby="sortby-dropdown" items="<?= esc(json_encode([ <DropdownMenu id="sortby-dropdown-menu" labelledby="sortby-dropdown" items="<?= esc(json_encode([
[ [
'type' => 'link', 'type' => 'link',
'title' => ($sortBy === 'activity' ? '✓ ' : '') . lang('Home.sort_options.activity'), 'title' => ($sortBy === 'activity' ? '✓ ' : '') . lang('Home.sort_options.activity'),
'uri' => route_to('home') . '?sort=activity', 'uri' => route_to('home') . '?sort=activity',
'class' => $sortBy === 'activity' ? 'font-semibold' : '', 'class' => $sortBy === 'activity' ? 'font-semibold' : '',
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => ($sortBy === 'created_desc' ? '✓ ' : '') . lang('Home.sort_options.created_desc'), 'title' => ($sortBy === 'created_desc' ? '✓ ' : '') . lang('Home.sort_options.created_desc'),
'uri' => route_to('home') . '?sort=created_desc', 'uri' => route_to('home') . '?sort=created_desc',
'class' => $sortBy === 'created_desc' ? 'font-semibold' : '', 'class' => $sortBy === 'created_desc' ? 'font-semibold' : '',
], ],
[ [
'type' => 'link', 'type' => 'link',
'title' => ($sortBy === 'created_asc' ? '✓ ' : '') . lang('Home.sort_options.created_asc'), 'title' => ($sortBy === 'created_asc' ? '✓ ' : '') . lang('Home.sort_options.created_asc'),
'uri' => route_to('home') . '?sort=created_asc', 'uri' => route_to('home') . '?sort=created_asc',
'class' => $sortBy === 'created_asc' ? 'font-semibold' : '', 'class' => $sortBy === 'created_asc' ? 'font-semibold' : '',
], ],
])) ?>" /> ])) ?>" />
</div> </div>
<div class="grid gap-4 mt-4 grid-cols-cards"> <div class="grid gap-4 mt-4 grid-cols-cards">
<?php if ($podcasts): ?> <?php if ($podcasts): ?>
@ -105,8 +105,8 @@
<footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t border-subtle"> <footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t border-subtle">
<?= render_page_links() ?> <?= render_page_links() ?>
<small><?= lang('Common.powered_by', [ <small><?= lang('Common.powered_by', [
'castopod' => 'castopod' =>
'<a class="inline-flex font-semibold hover:underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('castopod', 'ml-1 text-lg', 'social') . '</a>', '<a class="inline-flex font-semibold hover:underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('castopod', 'ml-1 text-lg', 'social') . '</a>',
], null, false) ?></small> ], null, false) ?></small>
</footer> </footer>
</body> </body>

View File

@ -42,9 +42,9 @@
<div class="container flex flex-col items-start px-2 py-4 mx-auto"> <div class="container flex flex-col items-start px-2 py-4 mx-auto">
<a href="<?= route_to('home') ?>" <a href="<?= route_to('home') ?>"
class="inline-flex items-center mb-2 text-sm focus:ring-accent"><?= icon( class="inline-flex items-center mb-2 text-sm focus:ring-accent"><?= icon(
'arrow-left', 'arrow-left',
'mr-2', 'mr-2',
) . lang('Page.back_to_home') ?></a> ) . lang('Page.back_to_home') ?></a>
<Heading tagName="h1" size="large"><?= esc($page->title) ?></Heading> <Heading tagName="h1" size="large"><?= esc($page->title) ?></Heading>
</div> </div>
</header> </header>
@ -54,8 +54,8 @@
<footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t border-subtle"> <footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t border-subtle">
<?= render_page_links() ?> <?= render_page_links() ?>
<small><?= lang('Common.powered_by', [ <small><?= lang('Common.powered_by', [
'castopod' => 'castopod' =>
'<a class="underline hover:no-underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod</a>', '<a class="underline hover:no-underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod</a>',
], null, false) ?></small> ], null, false) ?></small>
</footer> </footer>
</body> </body>

View File

@ -45,9 +45,9 @@
<div class="container flex flex-col items-start px-2 py-4 mx-auto"> <div class="container flex flex-col items-start px-2 py-4 mx-auto">
<a href="<?= route_to('home') ?>" <a href="<?= route_to('home') ?>"
class="inline-flex items-center mb-2 text-sm focus:ring-accent"><?= icon( class="inline-flex items-center mb-2 text-sm focus:ring-accent"><?= icon(
'arrow-left', 'arrow-left',
'mr-2', 'mr-2',
) . lang('Page.back_to_home') ?></a> ) . lang('Page.back_to_home') ?></a>
<Heading tagName="h1" size="large"><?= lang('Page.map.title') ?></Heading> <Heading tagName="h1" size="large"><?= lang('Page.map.title') ?></Heading>
</div> </div>
</header> </header>
@ -57,8 +57,8 @@
<footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right"> <footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right">
<?= render_page_links() ?> <?= render_page_links() ?>
<small><?= lang('Common.powered_by', [ <small><?= lang('Common.powered_by', [
'castopod' => 'castopod' =>
'<a class="inline-flex font-semibold hover:underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('castopod', 'ml-1 text-lg', 'social') . '</a>', '<a class="inline-flex font-semibold hover:underline focus:ring-accent" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod' . icon('castopod', 'ml-1 text-lg', 'social') . '</a>',
], null, false) ?></small> ], null, false) ?></small>
</footer> </footer>
</body> </body>

View File

@ -62,18 +62,18 @@
<button class="inline-flex items-center px-4 text-xs font-semibold leading-8 tracking-wider text-red-600 uppercase bg-white rounded-full shadow hover:text-red-500 focus:ring-accent" data-toggle="funding-links" data-toggle-class="hidden"><Icon glyph="heart" class="mr-2 text-sm"></Icon><?= lang('Podcast.sponsor') ?></button> <button class="inline-flex items-center px-4 text-xs font-semibold leading-8 tracking-wider text-red-600 uppercase bg-white rounded-full shadow hover:text-red-500 focus:ring-accent" data-toggle="funding-links" data-toggle-class="hidden"><Icon glyph="heart" class="mr-2 text-sm"></Icon><?= lang('Podcast.sponsor') ?></button>
<?php endif; ?> <?php endif; ?>
<?= anchor_popup( <?= anchor_popup(
route_to('follow', esc($podcast->handle)), route_to('follow', esc($podcast->handle)),
icon( icon(
'social/castopod', 'social/castopod',
'mr-2 text-xl text-black/75 group-hover:text-black', 'mr-2 text-xl text-black/75 group-hover:text-black',
) . lang('Podcast.follow'), ) . lang('Podcast.follow'),
[ [
'width' => 420, 'width' => 420,
'height' => 620, 'height' => 620,
'class' => 'class' =>
'group inline-flex items-center px-4 text-xs tracking-wider font-semibold text-black uppercase rounded-full leading-8 shadow focus:ring-accent bg-white', 'group inline-flex items-center px-4 text-xs tracking-wider font-semibold text-black uppercase rounded-full leading-8 shadow focus:ring-accent bg-white',
], ],
) ?> ) ?>
</div> </div>
</header> </header>
<?= $this->include('podcast/_partials/navigation') ?> <?= $this->include('podcast/_partials/navigation') ?>

View File

@ -26,10 +26,10 @@
rel="noopener noreferrer" rel="noopener noreferrer"
class="inline-flex items-center w-full font-semibold text-accent-base hover:text-accent-hover focus:ring-accent"> class="inline-flex items-center w-full font-semibold text-accent-base hover:text-accent-hover focus:ring-accent">
<?= icon( <?= icon(
esc($fundingPlatform->slug), esc($fundingPlatform->slug),
'mr-2 flex-shrink-0', 'mr-2 flex-shrink-0',
$fundingPlatform->type $fundingPlatform->type
) . '<span class="truncate">' . esc($fundingPlatform->link_url) . '</span>' ?> ) . '<span class="truncate">' . esc($fundingPlatform->link_url) . '</span>' ?>
</a> </a>
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>

View File

@ -4,8 +4,8 @@ if ($podcast->is_premium): ?>
<?php <?php
$isUnlocked = service('premium_podcasts') $isUnlocked = service('premium_podcasts')
->isUnlocked($podcast->handle); ->isUnlocked($podcast->handle);
$shownIcon = $isUnlocked ? 'lock-unlock' : 'lock'; $shownIcon = $isUnlocked ? 'lock-unlock' : 'lock';
$hiddenIcon = $isUnlocked ? 'lock' : 'lock-unlock'; $hiddenIcon = $isUnlocked ? 'lock' : 'lock-unlock';
?> ?>
<div class="flex flex-col items-center justify-between col-start-2 px-2 py-1 mt-2 sm:px-1 md:mt-4 rounded-conditional-full gap-y-2 sm:flex-row bg-accent-base gap-x-2 text-accent-contrast"> <div class="flex flex-col items-center justify-between col-start-2 px-2 py-1 mt-2 sm:px-1 md:mt-4 rounded-conditional-full gap-y-2 sm:flex-row bg-accent-base gap-x-2 text-accent-contrast">
<p class="inline-flex items-center text-sm md:pl-4 gap-x-2"><?= $isUnlocked ? lang('PremiumPodcasts.banner_lock') : lang('PremiumPodcasts.banner_unlock') ?></p> <p class="inline-flex items-center text-sm md:pl-4 gap-x-2"><?= $isUnlocked ? lang('PremiumPodcasts.banner_lock') : lang('PremiumPodcasts.banner_unlock') ?></p>

View File

@ -14,20 +14,20 @@
<?php foreach ($podcast->socialPlatforms as $socialPlatform): ?> <?php foreach ($podcast->socialPlatforms as $socialPlatform): ?>
<?php if ($socialPlatform->is_visible): ?> <?php if ($socialPlatform->is_visible): ?>
<?= anchor( <?= anchor(
esc($socialPlatform->link_url), esc($socialPlatform->link_url),
icon( icon(
esc($socialPlatform->slug), esc($socialPlatform->slug),
'', '',
$socialPlatform->type $socialPlatform->type
), ),
[ [
'class' => 'text-2xl text-skin-muted hover:text-skin-base w-8 h-8 items-center inline-flex justify-center', 'class' => 'text-2xl text-skin-muted hover:text-skin-base w-8 h-8 items-center inline-flex justify-center',
'target' => '_blank', 'target' => '_blank',
'rel' => 'noopener noreferrer', 'rel' => 'noopener noreferrer',
'data-tooltip' => 'bottom', 'data-tooltip' => 'bottom',
'title' => $socialPlatform->label, 'title' => $socialPlatform->label,
], ],
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
@ -41,20 +41,20 @@
<?php foreach ($podcast->podcastingPlatforms as $podcastingPlatform): ?> <?php foreach ($podcast->podcastingPlatforms as $podcastingPlatform): ?>
<?php if ($podcastingPlatform->is_visible): ?> <?php if ($podcastingPlatform->is_visible): ?>
<?= anchor( <?= anchor(
esc($podcastingPlatform->link_url), esc($podcastingPlatform->link_url),
icon( icon(
esc($podcastingPlatform->slug), esc($podcastingPlatform->slug),
'', '',
$podcastingPlatform->type $podcastingPlatform->type
), ),
[ [
'class' => 'text-2xl text-skin-muted hover:text-skin-base w-8 h-8 items-center inline-flex justify-center', 'class' => 'text-2xl text-skin-muted hover:text-skin-base w-8 h-8 items-center inline-flex justify-center',
'target' => '_blank', 'target' => '_blank',
'rel' => 'noopener noreferrer', 'rel' => 'noopener noreferrer',
'data-tooltip' => 'bottom', 'data-tooltip' => 'bottom',
'title' => $podcastingPlatform->label, 'title' => $podcastingPlatform->label,
], ],
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
@ -64,9 +64,9 @@
<div class="flex flex-col"> <div class="flex flex-col">
<p><?= esc($podcast->copyright) ?></p> <p><?= esc($podcast->copyright) ?></p>
<p><?= lang('Common.powered_by', [ <p><?= lang('Common.powered_by', [
'castopod' => 'castopod' =>
'<a class="inline-flex font-semibold text-skin-muted hover:underline focus:ring-accent" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('castopod', 'ml-1 text-lg', 'social') . '</a>', '<a class="inline-flex font-semibold text-skin-muted hover:underline focus:ring-accent" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('castopod', 'ml-1 text-lg', 'social') . '</a>',
], null, false) ?></p> ], null, false) ?></p>
</div> </div>
</footer> </footer>
</div> </div>

View File

@ -22,9 +22,10 @@
<?php $i = 0; ?> <?php $i = 0; ?>
<?php foreach ($podcast->persons as $person): ?> <?php foreach ($podcast->persons as $person): ?>
<img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= esc($person->full_name) ?>" class="object-cover w-8 -ml-4 border-2 rounded-full aspect-square bg-header border-background-base last:ml-0" loading="lazy" /> <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= esc($person->full_name) ?>" class="object-cover w-8 -ml-4 border-2 rounded-full aspect-square bg-header border-background-base last:ml-0" loading="lazy" />
<?php $i++; if ($i === 3) { <?php $i++;
break; if ($i === 3) {
}?> break;
}?>
<?php endforeach; ?> <?php endforeach; ?>
</span> </span>
<?= lang('Podcast.persons', [ <?= lang('Podcast.persons', [

View File

@ -24,16 +24,16 @@
<nav id="episode-lists-dropdown-menu" class="flex flex-col py-2 rounded-lg shadow border-3 border-contrast bg-elevated" aria-labelledby="episode-lists-dropdown" data-dropdown="menu" data-dropdown-placement="bottom-end"> <nav id="episode-lists-dropdown-menu" class="flex flex-col py-2 rounded-lg shadow border-3 border-contrast bg-elevated" aria-labelledby="episode-lists-dropdown" data-dropdown="menu" data-dropdown-placement="bottom-end">
<?php foreach ($episodesNav as $link): ?> <?php foreach ($episodesNav as $link): ?>
<?= anchor( <?= anchor(
$link['route'], $link['route'],
$link['label'] . ' (' . $link['number_of_episodes'] . ')', $link['label'] . ' (' . $link['number_of_episodes'] . ')',
[ [
'class' => 'class' =>
'px-2 py-1 whitespace-nowrap hover:bg-highlight ' . 'px-2 py-1 whitespace-nowrap hover:bg-highlight ' .
($link['is_active'] ($link['is_active']
? 'font-semibold' ? 'font-semibold'
: 'text-skin-muted hover:text-skin-base'), : 'text-skin-muted hover:text-skin-base'),
], ],
) ?> ) ?>
<?php endforeach; ?> <?php endforeach; ?>
</nav> </nav>
<?php endif; ?> <?php endif; ?>
@ -41,15 +41,15 @@
<div class="flex flex-col mt-4 gap-y-4"> <div class="flex flex-col mt-4 gap-y-4">
<?php foreach ($episodes as $episode): ?> <?php foreach ($episodes as $episode): ?>
<?= view('episode/_partials/card', [ <?= view('episode/_partials/card', [
'episode' => $episode, 'episode' => $episode,
'podcast' => $podcast, 'podcast' => $podcast,
]) ?> ]) ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<h1 class="px-4 mb-2 text-xl text-center"><?= lang( <h1 class="px-4 mb-2 text-xl text-center"><?= lang(
'Podcast.no_episode', 'Podcast.no_episode',
) ?></h1> ) ?></h1>
<?php endif; ?> <?php endif; ?>
<?= $this->endSection() <?= $this->endSection()

View File

@ -3,122 +3,122 @@
<form action="<?= route_to('post-attempt-action', esc(interact_as_actor()->username), $post->id) ?>" method="POST" class="flex justify-around w-full"> <form action="<?= route_to('post-attempt-action', esc(interact_as_actor()->username), $post->id) ?>" method="POST" class="flex justify-around w-full">
<?= csrf_field() ?> <?= csrf_field() ?>
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $post->id), route_to('post', esc($podcast->handle), $post->id),
icon('chat', 'text-2xl mr-1 opacity-40') . $post->replies_count, icon('chat', 'text-2xl mr-1 opacity-40') . $post->replies_count,
[ [
'class' => 'inline-flex items-center hover:underline', 'class' => 'inline-flex items-center hover:underline',
'title' => lang('Post.replies', [ 'title' => lang('Post.replies', [
'numberOfReplies' => $post->replies_count, 'numberOfReplies' => $post->replies_count,
]), ]),
], ],
) ?> ) ?>
<button type="submit" name="action" value="reblog" class="inline-flex items-center hover:underline" title="<?= lang( <button type="submit" name="action" value="reblog" class="inline-flex items-center hover:underline" title="<?= lang(
'Post.reblogs', 'Post.reblogs',
[ [
'numberOfReblogs' => $post->reblogs_count, 'numberOfReblogs' => $post->reblogs_count,
], ],
) ?>"><?= icon('repeat', 'text-2xl mr-1 opacity-40') . ) ?>"><?= icon('repeat', 'text-2xl mr-1 opacity-40') .
$post->reblogs_count ?></button> $post->reblogs_count ?></button>
<button type="submit" name="action" value="favourite" class="inline-flex items-center hover:underline" title="<?= lang( <button type="submit" name="action" value="favourite" class="inline-flex items-center hover:underline" title="<?= lang(
'Post.favourites', 'Post.favourites',
[ [
'numberOfFavourites' => $post->favourites_count, 'numberOfFavourites' => $post->favourites_count,
], ],
) ?>"><?= icon('heart', 'text-2xl mr-1 opacity-40') . ) ?>"><?= icon('heart', 'text-2xl mr-1 opacity-40') .
$post->favourites_count ?></button> $post->favourites_count ?></button>
<button id="<?= $post->id . <button id="<?= $post->id .
'-more-dropdown' ?>" type="button" class="px-2 py-1 text-2xl text-skin-muted focus:ring-accent" data-dropdown="button" data-dropdown-target="<?= $post->id . '-more-dropdown' ?>" type="button" class="px-2 py-1 text-2xl text-skin-muted focus:ring-accent" data-dropdown="button" data-dropdown-target="<?= $post->id .
'-more-dropdown-menu' ?>" aria-label="<?= lang( '-more-dropdown-menu' ?>" aria-label="<?= lang(
'Common.more', 'Common.more',
) ?>" aria-haspopup="true" aria-expanded="false"><?= icon('more') ?> ) ?>" aria-haspopup="true" aria-expanded="false"><?= icon('more') ?>
</button> </button>
</form> </form>
<nav id="<?= $post->id . <nav id="<?= $post->id .
'-more-dropdown-menu' ?>" class="flex flex-col py-2 text-sm rounded-lg shadow border-3 border-subtle bg-elevated" aria-labelledby="<?= $post->id . '-more-dropdown-menu' ?>" class="flex flex-col py-2 text-sm rounded-lg shadow border-3 border-subtle bg-elevated" aria-labelledby="<?= $post->id .
'-more-dropdown' ?>" data-dropdown="menu" data-dropdown-placement="bottom"> '-more-dropdown' ?>" data-dropdown="menu" data-dropdown-placement="bottom">
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $post->id), route_to('post', esc($podcast->handle), $post->id),
lang('Post.expand'), lang('Post.expand'),
[ [
'class' => 'px-4 py-1 hover:bg-highlight', 'class' => 'px-4 py-1 hover:bg-highlight',
], ],
) ?> ) ?>
<form action="<?= route_to( <form action="<?= route_to(
'post-attempt-block-actor', 'post-attempt-block-actor',
esc(interact_as_actor() esc(interact_as_actor()
->username), ->username),
$post->id, $post->id,
) ?>" method="POST"> ) ?>" method="POST">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang( <button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang(
'Post.block_actor', 'Post.block_actor',
[ [
'actorUsername' => esc($post->actor->username), 'actorUsername' => esc($post->actor->username),
], ],
) ?></button> ) ?></button>
</form> </form>
<form action="<?= route_to( <form action="<?= route_to(
'post-attempt-block-domain', 'post-attempt-block-domain',
esc(interact_as_actor() esc(interact_as_actor()
->username), ->username),
$post->id, $post->id,
) ?>" method="POST"> ) ?>" method="POST">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang( <button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang(
'Post.block_domain', 'Post.block_domain',
[ [
'actorDomain' => esc($post->actor->domain), 'actorDomain' => esc($post->actor->domain),
], ],
) ?></button> ) ?></button>
</form> </form>
<?php if ($post->actor->is_local): ?> <?php if ($post->actor->is_local): ?>
<hr class="my-2 border-subtle" /> <hr class="my-2 border-subtle" />
<form action="<?= route_to( <form action="<?= route_to(
'post-attempt-delete', 'post-attempt-delete',
esc($post->actor->username), esc($post->actor->username),
$post->id, $post->id,
) ?>" method="POST"> ) ?>" method="POST">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="w-full px-4 py-1 font-semibold text-left text-red-600 hover:bg-highlight"><?= lang( <button class="w-full px-4 py-1 font-semibold text-left text-red-600 hover:bg-highlight"><?= lang(
'Post.delete', 'Post.delete',
) ?></button> ) ?></button>
</form> </form>
<?php endif; ?> <?php endif; ?>
</nav> </nav>
<?php else: ?> <?php else: ?>
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $post->id), route_to('post', esc($podcast->handle), $post->id),
icon('chat', 'text-2xl mr-1 opacity-40') . $post->replies_count, icon('chat', 'text-2xl mr-1 opacity-40') . $post->replies_count,
[ [
'class' => 'inline-flex items-center hover:underline', 'class' => 'inline-flex items-center hover:underline',
'title' => lang('Post.replies', [ 'title' => lang('Post.replies', [
'numberOfReplies' => $post->replies_count, 'numberOfReplies' => $post->replies_count,
]), ]),
], ],
) ?> ) ?>
<?= anchor_popup( <?= anchor_popup(
route_to('post-remote-action', esc($podcast->handle), $post->id, 'reblog'), route_to('post-remote-action', esc($podcast->handle), $post->id, 'reblog'),
icon('repeat', 'text-2xl mr-1 opacity-40') . $post->reblogs_count, icon('repeat', 'text-2xl mr-1 opacity-40') . $post->reblogs_count,
[ [
'class' => 'inline-flex items-center hover:underline', 'class' => 'inline-flex items-center hover:underline',
'width' => 420, 'width' => 420,
'height' => 620, 'height' => 620,
'title' => lang('Post.reblogs', [ 'title' => lang('Post.reblogs', [
'numberOfReblogs' => $post->reblogs_count, 'numberOfReblogs' => $post->reblogs_count,
]), ]),
], ],
) ?> ) ?>
<?= anchor_popup( <?= anchor_popup(
route_to('post-remote-action', esc($podcast->handle), $post->id, 'favourite'), route_to('post-remote-action', esc($podcast->handle), $post->id, 'favourite'),
icon('heart', 'text-2xl mr-1 opacity-40') . $post->favourites_count, icon('heart', 'text-2xl mr-1 opacity-40') . $post->favourites_count,
[ [
'class' => 'inline-flex items-center hover:underline', 'class' => 'inline-flex items-center hover:underline',
'width' => 420, 'width' => 420,
'height' => 620, 'height' => 620,
'title' => lang('Post.favourites', [ 'title' => lang('Post.favourites', [
'numberOfFavourites' => $post->favourites_count, 'numberOfFavourites' => $post->favourites_count,
]), ]),
], ],
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
</footer> </footer>

View File

@ -37,17 +37,17 @@ if ($post->in_reply_to_id): ?>
</form> </form>
<?php else: ?> <?php else: ?>
<?= anchor_popup( <?= anchor_popup(
route_to('post-remote-action', esc($podcast->handle), $post->id, 'reply'), route_to('post-remote-action', esc($podcast->handle), $post->id, 'reply'),
lang('Post.reply_to', [ lang('Post.reply_to', [
'actorUsername' => esc($post->actor->username), 'actorUsername' => esc($post->actor->username),
]), ]),
[ [
'class' => 'class' =>
'text-center justify-center font-semibold rounded-full shadow relative z-10 px-4 py-2 w-full bg-accent-base text-accent-contrast inline-flex items-center hover:bg-accent-hover', 'text-center justify-center font-semibold rounded-full shadow relative z-10 px-4 py-2 w-full bg-accent-base text-accent-contrast inline-flex items-center hover:bg-accent-hover',
'width' => 420, 'width' => 420,
'height' => 620, 'height' => 620,
], ],
) ?> ) ?>
<?php endif; ?> <?php endif; ?>
</div> </div>

View File

@ -5,9 +5,9 @@ if ($preview_card->type === 'image'): ?>
<?php if ($preview_card->image): ?> <?php if ($preview_card->image): ?>
<div class="relative group"> <div class="relative group">
<?= icon( <?= icon(
'external-link', 'external-link',
'absolute inset-0 m-auto text-6xl bg-accent-base bg-opacity-50 group-hover:bg-opacity-100 text-accent-contrast rounded-full p-2', 'absolute inset-0 m-auto text-6xl bg-accent-base bg-opacity-50 group-hover:bg-opacity-100 text-accent-contrast rounded-full p-2',
) ?> ) ?>
<img src="<?= $preview_card->image ?>" alt="<?= esc($preview_card->title) ?>" class="object-cover w-full aspect-video" loading="lazy" /> <img src="<?= $preview_card->image ?>" alt="<?= esc($preview_card->title) ?>" class="object-cover w-full aspect-video" loading="lazy" />
</div> </div>
<?php endif; ?> <?php endif; ?>
@ -21,9 +21,9 @@ if ($preview_card->type === 'image'): ?>
<?php if ($preview_card->image): ?> <?php if ($preview_card->image): ?>
<div class="relative group"> <div class="relative group">
<?= icon( <?= icon(
'play', 'play',
'absolute inset-0 m-auto text-6xl bg-accent-base bg-opacity-50 group-hover:bg-opacity-100 text-accent-contrast rounded-full p-2', 'absolute inset-0 m-auto text-6xl bg-accent-base bg-opacity-50 group-hover:bg-opacity-100 text-accent-contrast rounded-full p-2',
) ?> ) ?>
<img class="object-cover w-full aspect-video" src="<?= $preview_card->image ?>" alt="<?= esc($preview_card->title) ?>" loading="lazy" /> <img class="object-cover w-full aspect-video" src="<?= $preview_card->image ?>" alt="<?= esc($preview_card->title) ?>" loading="lazy" />
</div> </div>
<?php endif; ?> <?php endif; ?>

View File

@ -1,44 +1,32 @@
<article class="relative z-10 w-full shadow bg-elevated rounded-conditional-2xl"> <article class="relative z-10 w-full shadow bg-elevated rounded-conditional-2xl">
<p class="inline-flex px-6 pt-4 text-xs text-skin-muted"><?= icon( <p class="inline-flex px-6 pt-4 text-xs text-skin-muted"><?= icon(
'repeat', 'repeat',
'text-lg mr-2 opacity-40', 'text-lg mr-2 opacity-40',
) . ) . lang('Post.actor_shared', [
lang('Post.actor_shared', [ 'actor' => esc($post->actor->display_name),
'actor' => esc($post->actor->display_name), ]) ?></p>
]) ?></p>
<header class="flex px-6 py-4 gap-x-2"> <header class="flex px-6 py-4 gap-x-2">
<img src="<?= $post->actor <img src="<?= $post->actor->avatar_image_url ?>" alt="<?= esc($post->actor->display_name) ?>" class="w-10 h-10 rounded-full aspect-square" loading="lazy" />
->avatar_image_url ?>" alt="<?= esc($post->actor->display_name) ?>" class="w-10 h-10 rounded-full aspect-square" loading="lazy" />
<div class="flex flex-col min-w-0"> <div class="flex flex-col min-w-0">
<a href="<?= $post->actor <a href="<?= $post->actor->uri ?>" class="flex items-baseline hover:underline" <?= $post->actor->is_local ? '' : 'target="_blank" rel="noopener noreferrer"' ?>>
->uri ?>" class="flex items-baseline hover:underline" <?= $post <span class="mr-2 font-semibold truncate"><?= esc($post->actor->display_name) ?></span>
->actor->is_local <span class="text-sm truncate text-skin-muted">@<?= esc($post->actor->username) . ($post->actor->is_local ? '' : '@' . $post->actor->domain) ?></span>
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>>
<span class="mr-2 font-semibold truncate"><?= esc($post->actor
->display_name) ?></span>
<span class="text-sm truncate text-skin-muted">@<?= esc($post->actor
->username) .
($post->actor->is_local
? ''
: '@' . $post->actor->domain) ?></span>
</a> </a>
<a href="<?= route_to('post', esc($podcast->handle), $post->id) ?>" <a href="<?= route_to('post', esc($podcast->handle), $post->id) ?>" class="text-xs text-skin-muted">
class="text-xs text-skin-muted">
<?= relative_time($post->published_at) ?> <?= relative_time($post->published_at) ?>
</a> </a>
</div> </div>
</header> </header>
<div class="px-6 mb-4 post-content"><?= $post->message_html ?></div> <div class="px-6 mb-4 post-content"><?= $post->message_html ?></div>
<?php if ($post->episode_id): ?> <?php if ($post->episode_id) : ?>
<?= view('episode/_partials/preview_card', [ <?= view('episode/_partials/preview_card', [
'index' => $index, 'index' => $index,
'episode' => $post->episode, 'episode' => $post->episode,
]) ?> ]) ?>
<?php elseif ($post->preview_card_id): ?> <?php elseif ($post->preview_card_id) : ?>
<?= view('post/_partials/preview_card', [ <?= view('post/_partials/preview_card', [
'preview_card' => $post->preview_card, 'preview_card' => $post->preview_card,
]) ?> ]) ?>
<?php endif; ?> <?php endif; ?>
<?= $this->include('post/_partials/actions') ?> <?= $this->include('post/_partials/actions') ?>
</article> </article>

View File

@ -5,85 +5,85 @@ if (can_user_interact()): ?>
<form action="<?= route_to('post-attempt-action', esc(interact_as_actor()->username), $reply->id) ?>" method="POST" class="flex items-start gap-x-6"> <form action="<?= route_to('post-attempt-action', esc(interact_as_actor()->username), $reply->id) ?>" method="POST" class="flex items-start gap-x-6">
<?= csrf_field() ?> <?= csrf_field() ?>
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $reply->id), route_to('post', esc($podcast->handle), $reply->id),
icon('chat', 'text-lg mr-1 opacity-40') . $reply->replies_count, icon('chat', 'text-lg mr-1 opacity-40') . $reply->replies_count,
[
'class' => 'inline-flex items-center hover:underline text-sm',
'title' => lang('Post.replies', [
'numberOfReplies' => $reply->replies_count,
]),
],
) ?>
<button type="submit" name="action" value="reblog" class="inline-flex items-center text-sm hover:underline" title="<?= lang(
'Post.reblogs',
[
'numberOfReblogs' => $reply->reblogs_count,
],
) ?>"><?= icon('repeat', 'text-lg mr-1 opacity-40') .
$reply->reblogs_count ?></button>
<button type="submit" name="action" value="favourite" class="inline-flex items-center text-sm hover:underline" title="<?= lang(
'Post.favourites',
[ [
'numberOfFavourites' => $reply->favourites_count, 'class' => 'inline-flex items-center hover:underline text-sm',
'title' => lang('Post.replies', [
'numberOfReplies' => $reply->replies_count,
]),
], ],
) ?>"><?= icon('heart', 'text-lg mr-1 opacity-40') . ) ?>
$reply->favourites_count ?></button> <button type="submit" name="action" value="reblog" class="inline-flex items-center text-sm hover:underline" title="<?= lang(
'Post.reblogs',
[
'numberOfReblogs' => $reply->reblogs_count,
],
) ?>"><?= icon('repeat', 'text-lg mr-1 opacity-40') .
$reply->reblogs_count ?></button>
<button type="submit" name="action" value="favourite" class="inline-flex items-center text-sm hover:underline" title="<?= lang(
'Post.favourites',
[
'numberOfFavourites' => $reply->favourites_count,
],
) ?>"><?= icon('heart', 'text-lg mr-1 opacity-40') .
$reply->favourites_count ?></button>
<button id="<?= $reply->id . <button id="<?= $reply->id .
'-more-dropdown' ?>" type="button" class="text-xl text-skin-muted focus:ring-accent" data-dropdown="button" data-dropdown-target="<?= $reply->id . '-more-dropdown' ?>" type="button" class="text-xl text-skin-muted focus:ring-accent" data-dropdown="button" data-dropdown-target="<?= $reply->id .
'-more-dropdown-menu' ?>" aria-label="<?= lang( '-more-dropdown-menu' ?>" aria-label="<?= lang(
'Common.more', 'Common.more',
) ?>" aria-haspopup="true" aria-expanded="false"><?= icon('more') ?> ) ?>" aria-haspopup="true" aria-expanded="false"><?= icon('more') ?>
</button> </button>
</form> </form>
<nav id="<?= $reply->id . <nav id="<?= $reply->id .
'-more-dropdown-menu' ?>" class="flex flex-col py-2 text-sm rounded-lg shadow border-3 border-subtle bg-elevated" aria-labelledby="<?= $reply->id . '-more-dropdown-menu' ?>" class="flex flex-col py-2 text-sm rounded-lg shadow border-3 border-subtle bg-elevated" aria-labelledby="<?= $reply->id .
'-more-dropdown' ?>" data-dropdown="menu" data-dropdown-placement="bottom"> '-more-dropdown' ?>" data-dropdown="menu" data-dropdown-placement="bottom">
<?= anchor( <?= anchor(
route_to('post', esc($podcast->handle), $reply->id), route_to('post', esc($podcast->handle), $reply->id),
lang('Post.expand'), lang('Post.expand'),
[ [
'class' => 'px-4 py-1 hover:bg-highlight', 'class' => 'px-4 py-1 hover:bg-highlight',
], ],
) ?> ) ?>
<form action="<?= route_to( <form action="<?= route_to(
'post-attempt-block-actor', 'post-attempt-block-actor',
esc(interact_as_actor() esc(interact_as_actor()
->username), ->username),
$reply->id, $reply->id,
) ?>" method="POST"> ) ?>" method="POST">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang( <button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang(
'Post.block_actor', 'Post.block_actor',
[ [
'actorUsername' => esc($reply->actor->username), 'actorUsername' => esc($reply->actor->username),
], ],
) ?></button> ) ?></button>
</form> </form>
<form action="<?= route_to( <form action="<?= route_to(
'post-attempt-block-domain', 'post-attempt-block-domain',
esc(interact_as_actor() esc(interact_as_actor()
->username), ->username),
$reply->id, $reply->id,
) ?>" method="POST"> ) ?>" method="POST">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang( <button class="w-full px-4 py-1 text-left hover:bg-highlight"><?= lang(
'Post.block_domain', 'Post.block_domain',
[ [
'actorDomain' => esc($reply->actor->domain), 'actorDomain' => esc($reply->actor->domain),
], ],
) ?></button> ) ?></button>
</form> </form>
<?php if ($reply->actor->is_local): ?> <?php if ($reply->actor->is_local): ?>
<hr class="my-2 border-subtle" /> <hr class="my-2 border-subtle" />
<form action="<?= route_to( <form action="<?= route_to(
'post-attempt-delete', 'post-attempt-delete',
esc($reply->actor->username), esc($reply->actor->username),
$reply->id, $reply->id,
) ?>" method="POST"> ) ?>" method="POST">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="w-full px-4 py-1 font-semibold text-left text-red-600 hover:bg-highlight"><?= lang( <button class="w-full px-4 py-1 font-semibold text-left text-red-600 hover:bg-highlight"><?= lang(
'Post.delete', 'Post.delete',
) ?></button> ) ?></button>
</form> </form>
<?php endif; ?> <?php endif; ?>
</nav> </nav>

View File

@ -4,18 +4,18 @@
<nav class="py-2"> <nav class="py-2">
<a href="<?= route_to('podcast-activity', esc($podcast->handle)) ?>" <a href="<?= route_to('podcast-activity', esc($podcast->handle)) ?>"
class="inline-flex items-center px-4 py-2 text-sm focus:ring-accent"><?= icon( class="inline-flex items-center px-4 py-2 text-sm focus:ring-accent"><?= icon(
'arrow-left', 'arrow-left',
'mr-2 text-lg', 'mr-2 text-lg',
) . ) .
lang('Post.back_to_actor_posts', [ lang('Post.back_to_actor_posts', [
'actor' => esc($post->actor->display_name), 'actor' => esc($post->actor->display_name),
]) ?></a> ]) ?></a>
</nav> </nav>
<div class="pb-12"> <div class="pb-12">
<?= view('post/_partials/post_with_replies', [ <?= view('post/_partials/post_with_replies', [
'index' => 1, 'index' => 1,
'post' => $post, 'post' => $post,
'podcast' => $podcast, 'podcast' => $podcast,
]) ?> ]) ?>
</div> </div>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@ -18,15 +18,15 @@
<body class="flex flex-col items-center justify-center min-h-screen mx-auto bg-base"> <body class="flex flex-col items-center justify-center min-h-screen mx-auto bg-base">
<header class="mb-4"> <header class="mb-4">
<a href="<?= route_to( <a href="<?= route_to(
'home', 'home',
) ?>" class="inline-flex items-baseline text-4xl font-bold font-display text-accent-base focus:ring-accent"> ) ?>" class="inline-flex items-baseline text-4xl font-bold font-display text-accent-base focus:ring-accent">
<?= 'castopod' . svg('castopod-logo', 'h-8 ml-2') ?> <?= 'castopod' . svg('castopod-logo', 'h-8 ml-2') ?>
</a> </a>
</header> </header>
<main class="flex flex-col w-full max-w-md px-6 py-4 mx-auto rounded-lg bg-elevated border-3 border-subtle gap-y-4"> <main class="flex flex-col w-full max-w-md px-6 py-4 mx-auto rounded-lg bg-elevated border-3 border-subtle gap-y-4">
<Heading tagName="h1" size="large" class="self-center"><?= $this->renderSection( <Heading tagName="h1" size="large" class="self-center"><?= $this->renderSection(
'title', 'title',
) ?></Heading> ) ?></Heading>
<?= view('_message_block') ?> <?= view('_message_block') ?>
<?= $this->renderSection('content') ?> <?= $this->renderSection('content') ?>
</main> </main>

View File

@ -53,10 +53,10 @@
<p class="py-4 text-sm text-center"> <p class="py-4 text-sm text-center">
<?= lang( <?= lang(
'Auth.haveAccount', 'Auth.haveAccount',
) ?> <a class="underline hover:no-underline" href="<?= route_to( ) ?> <a class="underline hover:no-underline" href="<?= route_to(
'login', 'login',
) ?>"><?= lang('Auth.login') ?></a> ) ?>"><?= lang('Auth.login') ?></a>
</p> </p>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@ -12,8 +12,8 @@
</div> </div>
<p class="mt-2 text-sm text-skin-muted"><?= lang( <p class="mt-2 text-sm text-skin-muted"><?= lang(
'Install.form.cache_config_hint', 'Install.form.cache_config_hint',
) ?></p> ) ?></p>
</div> </div>
<Forms.Field <Forms.Field
@ -21,10 +21,10 @@
name="cache_handler" name="cache_handler"
label="<?= lang('Install.form.cache_handler') ?>" label="<?= lang('Install.form.cache_handler') ?>"
options="<?= esc(json_encode([ options="<?= esc(json_encode([
'file' => lang('Install.form.cacheHandlerOptions.file'), 'file' => lang('Install.form.cacheHandlerOptions.file'),
'redis' => lang('Install.form.cacheHandlerOptions.redis'), 'redis' => lang('Install.form.cacheHandlerOptions.redis'),
'predis' => lang('Install.form.cacheHandlerOptions.predis'), 'predis' => lang('Install.form.cacheHandlerOptions.predis'),
])) ?>" ])) ?>"
selected="file" selected="file"
required="true" /> required="true" />

View File

@ -9,13 +9,13 @@
<div class="flex items-center"> <div class="flex items-center">
<span class="inline-flex items-center justify-center w-12 h-12 mr-2 text-sm font-semibold tracking-wider border-4 rounded-full text-accent-base border-accent-base">2/4</span> <span class="inline-flex items-center justify-center w-12 h-12 mr-2 text-sm font-semibold tracking-wider border-4 rounded-full text-accent-base border-accent-base">2/4</span>
<Heading tagName="h1"><?= lang( <Heading tagName="h1"><?= lang(
'Install.form.database_config', 'Install.form.database_config',
) ?></Heading> ) ?></Heading>
</div> </div>
<p class="mt-2 text-sm text-skin-muted"><?= lang( <p class="mt-2 text-sm text-skin-muted"><?= lang(
'Install.form.database_config_hint', 'Install.form.database_config_hint',
) ?></p> ) ?></p>
</div> </div>
<Forms.Field <Forms.Field