Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • adaures/castopod
  • mkljczk/castopod-host
  • spaetz/castopod-host
  • PatrykMis/castopod
  • jonas/castopod
  • ajeremias/castopod
  • misuzu/castopod
  • KrzysztofDomanczyk/castopod
  • Behel/castopod
  • nebulon/castopod
  • ewen/castopod
  • NeoluxConsulting/castopod
  • nateritter/castopod-og
  • prcutler/castopod
14 results
Select Git revision
Show changes
Showing
with 112 additions and 981 deletions
<section class="flex flex-col">
<header class="flex justify-between py-2">
<h1 class="text-xl"><?= lang('Podcast.latest_episodes') ?></h1>
<a href="<?= route_to(
'episode-list',
$podcast->id,
) ?>" class="inline-flex items-center text-sm underline hover:no-underline">
<?= lang('Podcast.see_all_episodes') ?>
<?= icon('chevron-right', 'ml-2') ?>
</a>
</header>
<?php if ($episodes): ?>
<div class="flex justify-between p-2 space-x-4 overflow-x-auto">
<?php foreach ($episodes as $episode): ?>
<article class="flex flex-col flex-shrink-0 w-56 overflow-hidden bg-white border shadow rounded-xl">
<img
src="<?= $episode->image->thumbnail_url ?>"
alt="<?= $episode->title ?>" class="object-cover" />
<div class="flex items-start justify-between p-2">
<div class="flex flex-col min-w-0">
<a href="<?= route_to(
'episode-view',
$podcast->id,
$episode->id,
) ?>"
class="text-sm font-semibold truncate hover:underline"
><?= $episode->title ?>
</a>
<div class="text-xs">
<?= episode_numbering(
$episode->number,
$episode->season_number,
'font-semibold text-gray-600',
true,
) ?>
<?php if ($episode->published_at): ?>
<span class="mx-1"></span>
<time
pubdate
datetime="<?= $episode->published_at->format(
DateTime::ATOM,
) ?>"
title="<?= $episode->published_at ?>">
<?= lang('Common.mediumDate', [
$episode->published_at,
]) ?>
</time>
<?php endif; ?>
</div>
</div>
<button
type="button"
class="inline-flex items-center p-1 outline-none focus:ring"
id="more-dropdown-<?= $episode->id ?>"
data-dropdown="button"
data-dropdown-target="more-dropdown-<?= $episode->id ?>-menu"
aria-label="<?= lang('Common.more') ?>"
aria-haspopup="true"
aria-expanded="false"
><?= icon('more') ?></button>
<nav
id="more-dropdown-<?= $episode->id ?>-menu"
class="z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border rounded shadow"
aria-labelledby="more-dropdown-<?= $episode->id ?>"
data-dropdown="menu"
data-dropdown-placement="bottom">
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'episode-edit',
$podcast->id,
$episode->id,
) ?>"><?= lang('Episode.edit') ?></a>
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'embeddable-player-add',
$podcast->id,
$episode->id,
) ?>"><?= lang(
'Episode.embeddable_player.add',
) ?></a>
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'episode-person-manage',
$podcast->id,
$episode->id,
) ?>"><?= lang('Person.persons') ?></a>
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'episode',
$podcast->name,
$episode->slug,
) ?>"><?= lang('Episode.go_to_page') ?></a>
<hr class="my-2 border border-gray-100">
<a class="px-4 py-1 font-semibold text-red-600 hover:bg-gray-100" href="<?= route_to(
'episode-delete',
$podcast->id,
$episode->id,
) ?>"><?= lang('Episode.delete') ?></a>
</nav>
</div>
</article>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="italic"><?= lang('Podcast.no_episode') ?></p>
<?php endif; ?>
</section>
\ No newline at end of file
<?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?>
<?= lang('Podcast.all_podcasts') ?>
<?= $this->endSection() ?>
<?= $this->section('pageTitle') ?>
<?= lang('Podcast.all_podcasts') ?> (<?= count($podcasts) ?>)
<?= $this->endSection() ?>
<?= $this->section('headerRight') ?>
<?= button(
lang('Podcast.create'),
route_to('podcast-create'),
['variant' => 'accent', 'iconLeft' => 'add'],
['class' => 'mr-2'],
) ?>
<?= button(lang('Podcast.import'), route_to('podcast-import'), [
'variant' => 'primary',
'iconLeft' => 'download',
]) ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="grid gap-4 grid-cols-podcasts">
<?php if ($podcasts !== null): ?>
<?php foreach ($podcasts as $podcast): ?>
<article class="h-full overflow-hidden bg-white border shadow rounded-xl">
<img
alt="<?= $podcast->title ?>"
src="<?= $podcast->image
->medium_url ?>" class="object-cover w-full h-48" />
<a href="<?= route_to(
'podcast-view',
$podcast->id,
) ?>" class="flex flex-col p-2 hover:underline">
<h2 class="font-semibold truncate"><?= $podcast->title ?></h2>
<p class="text-gray-600">@<?= $podcast->name ?></p>
</a>
<footer class="flex items-center justify-end p-2">
<a class="inline-flex p-2 mr-2 text-blue-700 bg-blue-100 rounded-full shadow-xs hover:bg-blue-200" href="<?= route_to(
'podcast-edit',
$podcast->id,
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
'Podcast.edit',
) ?>"><?= icon('edit') ?></a>
<a class="inline-flex p-2 text-gray-700 bg-gray-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
'podcast-view',
$podcast->id,
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
'Podcast.view',
) ?>"><?= icon('eye') ?></a>
</footer>
</article>
<?php endforeach; ?>
<?php else: ?>
<p class="italic"><?= lang('Podcast.no_podcast') ?></p>
<?php endif; ?>
</div>
<?= $this->endSection() ?>
<?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?>
<?= lang('Platforms.title') ?>
<?= $this->endSection() ?>
<?= $this->section('pageTitle') ?>
<?= lang('Platforms.title') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= form_open(route_to('platforms-save', $podcast->id, $platformType), [
'class' => 'flex flex-col max-w-md',
]) ?>
<?= csrf_field() ?>
<?php foreach ($platforms as $platform): ?>
<div class="relative flex items-start mb-8">
<div class="flex flex-col items-center w-12 mr-4">
<?= anchor(
$platform->submit_url,
icon(
$platform->type . '/' . $platform->slug,
'text-gray-600 text-4xl',
),
[
'class' => 'mb-1 text-gray-600 hover:text-gray-900',
'target' => '_blank',
'rel' => 'noopener noreferrer',
'data-toggle' => 'tooltip',
'data-placement' => 'bottom',
'title' => lang('Platforms.submit_url', [
'platformName' => $platform->label,
]),
],
) ?>
<div class="inline-flex bg-gray-200">
<?= anchor($platform->home_url, icon('external-link', 'mx-auto'), [
'class' => 'flex-1 text-gray-600 hover:text-gray-900',
'target' => '_blank',
'rel' => 'noopener noreferrer',
'data-toggle' => 'tooltip',
'data-placement' => 'bottom',
'title' => lang('Platforms.home_url', [
'platformName' => $platform->label,
]),
]) ?>
<?= $platform->submit_url
? anchor($platform->submit_url, icon('add', 'mx-auto'), [
'class' => 'flex-1 text-gray-600 hover:text-gray-900',
'target' => '_blank',
'rel' => 'noopener noreferrer',
'data-toggle' => 'tooltip',
'data-placement' => 'bottom',
'title' => lang('Platforms.submit_url', [
'platformName' => $platform->label,
]),
])
: '' ?>
</div>
</div>
<div class="flex flex-col flex-1">
<?= $platform->link_url
? anchor(
route_to(
'podcast-platform-remove',
$podcast->id,
$platform->slug,
),
icon('delete-bin', 'mx-auto'),
[
'class' =>
'absolute right-0 p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900',
'data-toggle' => 'tooltip',
'data-placement' => 'bottom',
'title' => lang('Platforms.remove', [
'platformName' => $platform->label,
]),
],
)
: '' ?>
<?= form_label($platform->label, $platform->slug, [
'class' => 'font-semibold mb-2',
]) ?>
<?= form_input([
'id' => $platform->slug . '_link_url',
'name' => 'platforms[' . $platform->slug . '][url]',
'class' => 'form-input mb-1 w-full',
'value' => old($platform->slug . '_link_url', $platform->link_url),
'type' => 'url',
'placeholder' => 'https://...',
]) ?>
<?= form_input([
'id' => $platform->slug . '_link_content',
'name' => 'platforms[' . $platform->slug . '][content]',
'class' => 'form-input mb-1 w-full',
'value' => old(
$platform->slug . '_link_content',
$platform->link_content,
),
'type' => 'text',
'placeholder' => lang("Platforms.description.{$platform->type}"),
]) ?>
<?= form_switch(
lang('Platforms.visible'),
[
'id' => $platform->slug . '_visible',
'name' => 'platforms[' . $platform->slug . '][visible]',
],
'yes',
old(
$platform->slug . '_visible',
$platform->is_visible ? $platform->is_visible : false,
),
'text-sm mb-1',
) ?>
<?= form_switch(
lang('Platforms.on_embeddable_player'),
[
'id' => $platform->slug . '_on_embeddable_player',
'name' =>
'platforms[' . $platform->slug . '][on_embeddable_player]',
],
'yes',
old(
$platform->slug . '_on_embeddable_player',
$platform->is_on_embeddable_player
? $platform->is_on_embeddable_player
: false,
),
'text-sm',
) ?>
</div>
</div>
<?php endforeach; ?>
<?= button(
lang('Platforms.submit'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?>
<?= $podcast->title ?>
<?= $this->endSection() ?>
<?= $this->section('pageTitle') ?>
<?= $podcast->title ?>
<?= $this->endSection() ?>
<?= $this->section('headerLeft') ?>
<?= location_link($podcast->location, 'ml-4 text-sm') ?>
<?= $this->endSection() ?>
<?= $this->section('headerRight') ?>
<?= button(
lang('Podcast.edit'),
route_to('podcast-edit', $podcast->id),
['variant' => 'primary', 'iconLeft' => 'edit'],
['class' => 'mr-2'],
) ?>
<?= button(lang('Episode.create'), route_to('episode-create', $podcast->id), [
'variant' => 'accent',
'iconLeft' => 'add',
]) ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= view_cell('\App\Controllers\Admin\PodcastController::latestEpisodes', [
'limit' => 5,
'podcast_id' => $podcast->id,
]) ?>
<?= $this->endSection() ?>
<?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?>
<?= lang('User.create') ?>
<?= $this->endSection() ?>
<?= $this->section('pageTitle') ?>
<?= lang('User.create') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= form_open(route_to('user-create'), ['class' => 'flex flex-col max-w-sm']) ?>
<?= csrf_field() ?>
<?= form_label(lang('User.form.email'), 'email') ?>
<?= form_input([
'id' => 'email',
'name' => 'email',
'class' => 'form-input mb-4',
'value' => old('email'),
'type' => 'email',
]) ?>
<?= form_label(lang('User.form.username'), 'username') ?>
<?= form_input([
'id' => 'username',
'name' => 'username',
'class' => 'form-input mb-4',
'value' => old('username'),
]) ?>
<?= form_label(lang('User.form.password'), 'password') ?>
<?= form_input([
'id' => 'password',
'name' => 'password',
'class' => 'form-input mb-4',
'type' => 'password',
'autocomplete' => 'new-password',
]) ?>
<?= button(
lang('User.form.submit_create'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?>
<?= lang('User.edit_roles', ['username' => $user->username]) ?>
<?= $this->endSection() ?>
<?= $this->section('pageTitle') ?>
<?= lang('User.edit_roles', ['username' => $user->username]) ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= form_open(route_to('user-edit', $user->id), [
'class' => 'flex flex-col max-w-sm',
]) ?>
<?= csrf_field() ?>
<?= form_label(lang('User.form.roles'), 'roles') ?>
<?= form_multiselect('roles[]', $roleOptions, $user->roles, [
'id' => 'roles',
'class' => 'mb-4',
]) ?>
<?= button(
lang('User.form.submit_edit'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?>
<?= lang('User.all_users') ?>
<?= $this->endSection() ?>
<?= $this->section('pageTitle') ?>
<?= lang('User.all_users') ?> (<?= count($users) ?>)
<?= $this->endSection() ?>
<?= $this->section('headerRight') ?>
<?= button(lang('User.create'), route_to('user-create'), [
'variant' => 'accent',
'iconLeft' => 'user-add',
]) ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= data_table(
[
[
'header' => lang('User.list.user'),
'cell' => function ($user) {
return '<div class="flex flex-col">' .
$user->username .
'<span class="text-sm text-gray-600">' .
$user->email .
'</span></div>';
},
],
[
'header' => lang('User.list.roles'),
'cell' => function ($user) {
return implode(',', $user->roles) .
icon_button(
'edit',
lang('User.edit_roles', [
'username' => $user->username,
]),
route_to('user-edit', $user->id),
['variant' => 'info'],
['class' => 'ml-2'],
);
},
],
[
'header' => lang('User.list.banned'),
'cell' => function ($user) {
return $user->isBanned()
? lang('Common.yes')
: lang('Common.no');
},
],
[
'header' => lang('Common.actions'),
'cell' => function ($user) {
return button(
lang('User.forcePassReset'),
route_to('user-force_pass_reset', $user->id),
[
'variant' => 'secondary',
'size' => 'small',
],
['class' => 'mr-2'],
) .
button(
lang('User.' . ($user->isBanned() ? 'unban' : 'ban')),
route_to(
$user->isBanned() ? 'user-unban' : 'user-ban',
$user->id,
),
['variant' => 'warning', 'size' => 'small'],
['class' => 'mr-2'],
) .
button(
lang('User.delete'),
route_to('user-delete', $user->id),
['variant' => 'danger', 'size' => 'small'],
);
},
],
],
$users,
) ?>
<?= $this->endSection() ?>
<?= helper('svg') ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Castopod Auth</title>
<meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/index.css"/>
</head>
<body class="flex flex-col items-center justify-center min-h-screen mx-auto bg-gray-100">
<header class="mb-4">
<a href="<?= route_to(
'home',
) ?>" class="inline-flex items-baseline text-4xl font-bold font-display text-pine-700">
<?= 'castopod' . svg('castopod-logo', 'h-8 ml-2') ?>
</a>
</header>
<main class="w-full max-w-md px-6 py-4 mx-auto bg-white rounded-lg shadow">
<h1 class="mb-6 text-2xl font-bold text-center font-display"><?= $this->renderSection(
'title',
) ?></h1>
<?= view('_message_block') ?>
<?= $this->renderSection('content') ?>
</main>
<footer class="flex flex-col text-sm">
<?= $this->renderSection('footer') ?>
<small class="py-4 text-center border-t"><?= lang('Common.powered_by', [
'castopod' =>
'<a class="underline hover:no-underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod</a>',
]) ?></small>
</footer>
</body>
<p>This is activation email for your account on <?= base_url() ?>.</p>
<p>To activate your account use this URL.</p>
<p><a href="<?= base_url('activate-account') .
'?token=' .
$hash ?>">Activate account</a>.</p>
<br>
<p>If you did not registered on this website, you can safely ignore this email.</p>
<p>Someone requested a password reset at this email address for <?= base_url() ?>.</p>
<p>To reset the password use this code or URL and follow the instructions.</p>
<p>Your Code: <?= $hash ?></p>
<p>Visit the <a href="<?= base_url('reset-password') .
'?token=' .
$hash ?>">Reset Form</a>.</p>
<br>
<p>If you did not request a password reset, you can safely ignore this email.</p>
<?= helper('form') ?>
<?= $this->extend($config->viewLayout) ?>
<?= $this->section('title') ?>
<?= lang('Auth.forgotPassword') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<p class="mb-4 text-gray-600"><?= lang('Auth.enterEmailForInstructions') ?></p>
<?= form_open(route_to('forgot'), ['class' => 'flex flex-col']) ?>
<?= csrf_field() ?>
<?= form_label(lang('Auth.emailAddress'), 'email') ?>
<?= form_input([
'id' => 'email',
'name' => 'email',
'class' => 'form-input mb-4',
'type' => 'email',
'required' => 'required',
]) ?>
<?= button(
lang('Auth.sendInstructions'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= helper('form') ?>
<?= $this->extend($config->viewLayout) ?>
<?= $this->section('title') ?>
<?= lang('Auth.loginTitle') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= form_open(route_to('login'), ['class' => 'flex flex-col']) ?>
<?= csrf_field() ?>
<?= form_label(lang('Auth.emailOrUsername'), 'login') ?>
<?= form_input([
'id' => 'login',
'name' => 'login',
'class' => 'form-input mb-4',
'required' => 'required',
]) ?>
<?= form_label(lang('Auth.password'), 'password') ?>
<?= form_input([
'id' => 'password',
'name' => 'password',
'class' => 'form-input mb-4',
'type' => 'password',
'required' => 'required',
]) ?>
<?= button(
lang('Auth.loginAction'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= $this->section('footer') ?>
<div class="flex flex-col items-center py-4 text-sm text-center">
<?php if ($config->allowRegistration): ?>
<a class="underline hover:no-underline" href="<?= route_to(
'register',
) ?>"><?= lang('Auth.needAnAccount') ?></a>
<?php endif; ?>
<a class="underline hover:no-underline" href="<?= route_to(
'forgot',
) ?>"><?= lang('Auth.forgotYourPassword') ?></a>
</div>
<?= $this->endSection() ?>
<?= helper('form') ?>
<?= $this->extend($config->viewLayout) ?>
<?= $this->section('title') ?>
<?= lang('Auth.register') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= form_open(route_to('register'), ['class' => 'flex flex-col']) ?>
<?= csrf_field() ?>
<?= form_label(lang('Auth.email'), 'email') ?>
<?= form_input([
'id' => 'email',
'name' => 'email',
'class' => 'form-input',
'value' => old('email'),
'type' => 'email',
'required' => 'required',
'aria-describedby' => 'emailHelp',
]) ?>
<small id="emailHelp" class="mb-4 text-gray-700">
<?= lang('Auth.weNeverShare') ?>
</small>
<?= form_label(lang('Auth.username'), 'username') ?>
<?= form_input([
'id' => 'username',
'name' => 'username',
'class' => 'form-input mb-4',
'value' => old('username'),
'required' => 'required',
]) ?>
<?= form_label(lang('Auth.password'), 'password') ?>
<?= form_input([
'id' => 'password',
'name' => 'password',
'class' => 'form-input mb-4',
'type' => 'password',
'required' => 'required',
'autocomplete' => 'new-password',
]) ?>
<?= button(
lang('Auth.register'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= $this->section('footer') ?>
<p class="py-4 text-sm text-center">
<?= lang(
'Auth.alreadyRegistered',
) ?> <a class="underline hover:no-underline" href="<?= route_to(
'login',
) ?>"><?= lang('Auth.signIn') ?></a>
</p>
<?= $this->endSection() ?>
<?= helper('form') ?>
<?= $this->extend($config->viewLayout) ?>
<?= $this->section('title') ?>
<?= lang('Auth.resetYourPassword') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<p class="mb-4"><?= lang('Auth.enterCodeEmailPassword') ?></p>
<?= form_open(route_to('reset-password'), ['class' => 'flex flex-col']) ?>
<?= csrf_field() ?>
<?= form_label(lang('Auth.token'), 'token') ?>
<?= form_input([
'id' => 'token',
'name' => 'token',
'class' => 'form-input mb-4',
'value' => old('token', $token ?? ''),
'required' => 'required',
]) ?>
<?= form_label(lang('Auth.email'), 'email') ?>
<?= form_input([
'id' => 'email',
'name' => 'email',
'class' => 'form-input mb-4',
'value' => old('email'),
'required' => 'required',
'type' => 'email',
]) ?>
<?= form_label(lang('Auth.newPassword'), 'password') ?>
<?= form_input([
'id' => 'password',
'name' => 'password',
'class' => 'form-input mb-4',
'type' => 'password',
'required' => 'required',
'autocomplete' => 'new-password',
]) ?>
<?= button(
lang('Auth.resetPassword'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>
<?= $this->extend('_layout') ?>
<?= $this->section('title') ?>
<?= lang('Person.credits') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="grid w-full grid-cols-1 gap-4 md:grid-cols-2">
<?php foreach ($credits as $groupSlug => $groups): ?>
<?php if ($groupSlug): ?>
<div class="col-span-1 mt-12 mb-2 text-xl font-semibold text-gray-500 md:text-2xl md:col-span-2 "><?= $groups[
'group_label'
] ?></div>
<?php endif; ?>
<?php foreach ($groups['persons'] as $persons): ?>
<div class="flex mt-2 mb-2">
<img src="<?= $persons['thumbnail_url'] ?>" alt="<?= $persons[
'full_name'
] ?>" class="object-cover w-16 h-16 border-4 rounded-full md:h-24 md:w-24 border-gray" />
<div class="flex flex-col ml-3 mr-4">
<span class="text-lg font-semibold text-gray-700 md:text-xl">
<?= $persons['full_name'] ?>
</span>
<?php if ($persons['information_url'] !== null): ?>
<a href="<?= $persons[
'information_url'
] ?>" class="text-sm text-blue-800 hover:underline" target="_blank" rel="noreferrer noopener"><?= $persons[
'information_url'
] ?></a>
<?php endif; ?>
</div>
</div>
<div class="flex flex-col">
<?php foreach ($persons['roles'] as $role): ?>
<?= $role['role_label'] ?>
<?php foreach ($role['is_in'] as $in): ?>
<a href="<?= $in[
'link'
] ?>" class="text-sm text-gray-500 hover:underline"><?= $in[
'title'
] ?></a>
<?php endforeach; ?>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<?php endforeach; ?>
</div>
<?php $this->endSection(); ?>
<!DOCTYPE html>
<html lang="<?= service('request')->getLocale() ?>">
<head>
<meta charset="UTF-8" />
<title><?= $episode->title ?></title>
<meta name="description" content="<?= htmlspecialchars(
$episode->description,
) ?>" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/index.css" />
<link rel="canonical" href="<?= $episode->link ?>" />
</head>
<body class="flex w-full h-screen" style="background: <?= $theme[
'background'
] ?>; color: <?= $theme['text'] ?>;">
<img src="<?= $episode->image
->medium_url ?>" alt="<?= $episode->title ?>" class="flex-shrink h-full" />
<div class="flex flex-col flex-1 min-w-0 p-4">
<div class="flex items-center">
<a href="<?= route_to(
'podcast',
$podcast->name,
) ?>" style="color: <?= $theme[
'text'
] ?>;" class="mr-2 text-xs tracking-wider uppercase truncate opacity-75 hover:opacity-100" target="_blank">
<?= $podcast->title ?>
</a>
<a href="https://castopod.org/" class="ml-auto text-xl text-pine-700 hover:opacity-75" title="<?= lang(
'Common.powered_by',
[
'castopod' => 'Castopod',
],
) ?>" target="_blank" rel="noopener noreferrer">
<?= icon('podcasting/castopod') ?>
</a>
</div>
<a href="<?= $episode->link ?>" class="flex items-center mb-2" style="color: <?= $theme[
'text'
] ?>;" target="_blank">
<h1 class="mr-2 text-lg font-semibold truncate opacity-100 hover:opacity-75">
<?= $episode->title ?>
</h1>
<?= episode_numbering(
$episode->number,
$episode->season_number,
'text-xs',
true,
) ?>
</a>
<audio controls preload="none" class="flex w-full mt-auto">
<source src="<?= $episode->audio_file_analytics_url .
(isset($_SERVER['HTTP_REFERER'])
? '?_from=' .
parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)
: '') ?>" type="<?= $episode->audio_file_mimetype ?>" />
Your browser does not support the audio tag.
</audio>
</div>
</body>
</html>
<?php
declare(strict_types=1);
use CodeIgniter\CLI\CLI;
CLI::error('ERROR: ' . $code);
......
<?php
declare(strict_types=1);
use CodeIgniter\CLI\CLI;
// The main Exception
CLI::newLine();
CLI::write('[' . $exception::class . ']', 'light_gray', 'red');
CLI::newLine();
CLI::write($message);
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green'));
CLI::newLine();
CLI::write(
'at ' .
CLI::color(
clean_path($exception->getFile()) . ':' . $exception->getLine(),
'green',
),
);
CLI::newLine();
$last = $exception;
while ($prevException = $last->getPrevious()) {
$last = $prevException;
CLI::write(' Caused by:');
CLI::write(' [' . $prevException::class . ']', 'red');
CLI::write(' ' . $prevException->getMessage());
CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green'));
CLI::newLine();
}
// The backtrace
if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
$backtraces = $exception->getTrace();
$backtraces = $last->getTrace();
if ($backtraces) {
CLI::write('Backtrace:', 'green');
......@@ -28,46 +33,31 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
foreach ($backtraces as $i => $error) {
$padFile = ' '; // 4 spaces
$padClass = ' '; // 7 spaces
$c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT);
$c = str_pad((string) ($i + 1), 3, ' ', STR_PAD_LEFT);
if (isset($error['file'])) {
$filepath = clean_path($error['file']) . ':' . $error['line'];
$fileKey = clean_path($error['file']) . ':' . $error['line'];
CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
CLI::write($c . $padFile . CLI::color($fileKey, 'yellow'));
} else {
CLI::write(
$c . $padFile . CLI::color('[internal function]', 'yellow'),
);
CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'));
}
$function = '';
if (isset($error['class'])) {
$type =
$error['type'] === '->'
? '()' . $error['type']
: $error['type'];
$function .=
$padClass . $error['class'] . $type . $error['function'];
} elseif (!isset($error['class']) && isset($error['function'])) {
$type = ($error['type'] === '->') ? '()' . $error['type'] : $error['type'];
$function .= $padClass . $error['class'] . $type . $error['function'];
} elseif (! isset($error['class']) && isset($error['function'])) {
$function .= $padClass . $error['function'];
}
$args = implode(
', ',
array_map(function ($value) {
switch (true) {
case is_object($value):
return 'Object(' . $value::class . ')';
case is_array($value):
return count($value) > 0 ? '[...]' : '[]';
case is_null($value):
return 'null'; // return the lowercased version
default:
return var_export($value, true);
}
}, array_values($error['args'] ?? [])),
);
$args = implode(', ', array_map(static fn ($value) => match (true) {
is_object($value) => 'Object(' . $value::class . ')',
is_array($value) => $value !== [] ? '[...]' : '[]',
$value === null => 'null', // return the lowercased version
default => var_export($value, true),
}, array_values($error['args'] ?? [])));
$function .= '(' . $args . ')';
......
<?php
declare(strict_types=1);
// On the CLI, we still want errors in productions
// so just use the exception template.
include __DIR__ . '/error_exception.php';
:root {
--main-bg-color: #fff;
--main-text-color: #555;
--dark-text-color: #222;
--light-text-color: #c7c7c7;
--brand-primary-color: #e06e3f;
--light-bg-color: #ededee;
--dark-bg-color: #404040;
}
body {
height: 100%;
background: #fafafa;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #777;
background: var(--main-bg-color);
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji";
color: var(--main-text-color);
font-weight: 300;
margin: 0;
padding: 0;
}
h1 {
font-weight: lighter;
letter-spacing: 0.8;
font-size: 3rem;
color: #222;
color: var(--dark-text-color);
margin: 0;
}
h1.headline {
margin-top: 20%;
font-size: 5rem;
}
.text-center {
text-align: center;
}
p.lead {
font-size: 1.6rem;
}
.container {
max-width: 75rem;
margin: 0 auto;
padding: 1rem;
}
.header {
background: #85271f;
color: #fff;
background: var(--light-bg-color);
color: var(--dark-text-color);
margin-top: 2.17rem;
}
.header .container {
padding: 1rem;
}
.header h1 {
color: #fff;
font-size: 2.5rem;
font-weight: 500;
}
.header p {
font-size: 1.2rem;
margin: 0;
line-height: 2.5;
}
.header a {
color: rgba(255, 255, 255, 0.5);
color: var(--brand-primary-color);
margin-left: 2rem;
display: none;
text-decoration: none;
}
.header:hover a {
display: inline;
}
.footer .container {
border-top: 1px solid #e7e7e7;
margin-top: 1rem;
.environment {
background: var(--brand-primary-color);
color: var(--main-bg-color);
text-align: center;
padding: calc(4px + 0.2083vw);
width: 100%;
margin-top: -2.14rem;
position: fixed;
}
.source {
background: #333;
color: #c7c7c7;
background: #343434;
color: var(--light-text-color);
padding: 0.5em 1em;
border-radius: 5px;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 0.85rem;
margin: 0;
overflow-x: scroll;
}
.source span.line {
line-height: 1.4;
}
.source span.line .number {
color: #666;
}
.source .line .highlight {
display: block;
background: #555;
color: #fff;
background: var(--dark-text-color);
color: var(--light-text-color);
}
.source span.highlight .number {
color: #fff;
}
......@@ -88,36 +124,44 @@ p.lead {
padding: 0;
margin-bottom: -1px;
}
.tabs li {
display: inline;
}
.tabs a:link,
.tabs a:visited {
padding: 0rem 1rem;
padding: 0 1rem;
line-height: 2.7;
text-decoration: none;
color: #a7a7a7;
background: #f1f1f1;
border: 1px solid #e7e7e7;
color: var(--dark-text-color);
background: var(--light-bg-color);
border: 1px solid rgb(0 0 0 / 15%);
border-bottom: 0;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
display: inline-block;
}
.tabs a:hover {
background: #e7e7e7;
border-color: #e1e1e1;
background: var(--light-bg-color);
border-color: rgb(0 0 0 / 15%);
}
.tabs a.active {
background: #fff;
background: var(--main-bg-color);
color: var(--main-text-color);
}
.tab-content {
background: #fff;
border: 1px solid #efefef;
background: var(--main-bg-color);
border: 1px solid rgb(0 0 0 / 15%);
}
.content {
padding: 1rem;
}
.hide {
display: none;
}
......@@ -132,26 +176,26 @@ p.lead {
border-radius: 5px;
color: #31708f;
}
ul,
ol {
line-height: 1.8;
}
table {
width: 100%;
overflow: hidden;
}
th {
text-align: left;
border-bottom: 1px solid #e7e7e7;
padding-bottom: 0.5rem;
}
td {
padding: 0.2rem 0.5rem 0.2rem 0;
}
tr:hover td {
background: #f1f1f1;
}
td pre {
white-space: pre-wrap;
}
......@@ -159,20 +203,25 @@ td pre {
.trace a {
color: inherit;
}
.trace table {
width: auto;
}
.trace tr td:first-child {
min-width: 5em;
font-weight: bold;
}
.trace td {
background: #e7e7e7;
background: var(--light-bg-color);
padding: 0 1rem;
}
.trace td pre {
margin: 0;
}
.args {
display: none;
}