feat(person): add podcastindex.org namespace person tag
This commit is contained in:
parent
17e1e94a50
commit
8acd011f13
|
@ -30,6 +30,9 @@ $RECYCLE.BIN/
|
|||
# Linux
|
||||
*~
|
||||
|
||||
# vim
|
||||
*.swp
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
|
@ -135,6 +138,7 @@ node_modules
|
|||
# public folder
|
||||
public/*
|
||||
!public/media
|
||||
!public/media/~person
|
||||
!public/.htaccess
|
||||
!public/favicon.ico
|
||||
!public/index.php
|
||||
|
@ -144,6 +148,14 @@ public/*
|
|||
public/media/*
|
||||
!public/media/index.html
|
||||
|
||||
# public person folder
|
||||
public/media/~person/*
|
||||
!public/media/~person/index.html
|
||||
|
||||
# Generated files
|
||||
app/Language/en/PersonsTaxonomy.php
|
||||
app/Language/fr/PersonsTaxonomy.php
|
||||
|
||||
#-------------------------
|
||||
# Docker volumes
|
||||
#-------------------------
|
||||
|
|
|
@ -85,6 +85,37 @@ $routes->group(
|
|||
'as' => 'my-podcasts',
|
||||
]);
|
||||
|
||||
$routes->group('persons', function ($routes) {
|
||||
$routes->get('/', 'Person', [
|
||||
'as' => 'person-list',
|
||||
'filter' => 'permission:person-list',
|
||||
]);
|
||||
$routes->get('new', 'Person::create', [
|
||||
'as' => 'person-create',
|
||||
'filter' => 'permission:person-create',
|
||||
]);
|
||||
$routes->post('new', 'Person::attemptCreate', [
|
||||
'filter' => 'permission:person-create',
|
||||
]);
|
||||
$routes->group('(:num)', function ($routes) {
|
||||
$routes->get('/', 'Person::view/$1', [
|
||||
'as' => 'person-view',
|
||||
'filter' => 'permission:person-view',
|
||||
]);
|
||||
$routes->get('edit', 'Person::edit/$1', [
|
||||
'as' => 'person-edit',
|
||||
'filter' => 'permission:person-edit',
|
||||
]);
|
||||
$routes->post('edit', 'Person::attemptEdit/$1', [
|
||||
'filter' => 'permission:person-edit',
|
||||
]);
|
||||
$routes->add('delete', 'Person::delete/$1', [
|
||||
'as' => 'person-delete',
|
||||
'filter' => 'permission:person-delete',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// Podcasts
|
||||
$routes->group('podcasts', function ($routes) {
|
||||
$routes->get('/', 'Podcast::list', [
|
||||
|
@ -124,6 +155,25 @@ $routes->group(
|
|||
'filter' => 'permission:podcasts-delete',
|
||||
]);
|
||||
|
||||
$routes->group('persons', function ($routes) {
|
||||
$routes->get('/', 'PodcastPerson/$1', [
|
||||
'as' => 'podcast-person-manage',
|
||||
'filter' => 'permission:podcast-edit',
|
||||
]);
|
||||
$routes->post('/', 'PodcastPerson::attemptAdd/$1', [
|
||||
'filter' => 'permission:podcast-edit',
|
||||
]);
|
||||
|
||||
$routes->get(
|
||||
'(:num)/remove',
|
||||
'PodcastPerson::remove/$1/$2',
|
||||
[
|
||||
'as' => 'podcast-person-remove',
|
||||
'filter' => 'permission:podcast-edit',
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
$routes->group('analytics', function ($routes) {
|
||||
$routes->get('/', 'Podcast::viewAnalytics/$1', [
|
||||
'as' => 'podcast-analytics',
|
||||
|
@ -276,6 +326,30 @@ $routes->group(
|
|||
'filter' => 'permission:podcast_episodes-edit',
|
||||
]
|
||||
);
|
||||
|
||||
$routes->group('persons', function ($routes) {
|
||||
$routes->get('/', 'EpisodePerson/$1/$2', [
|
||||
'as' => 'episode-person-manage',
|
||||
'filter' => 'permission:podcast_episodes-edit',
|
||||
]);
|
||||
$routes->post(
|
||||
'/',
|
||||
'EpisodePerson::attemptAdd/$1/$2',
|
||||
[
|
||||
'filter' =>
|
||||
'permission:podcast_episodes-edit',
|
||||
]
|
||||
);
|
||||
$routes->get(
|
||||
'(:num)/remove',
|
||||
'EpisodePerson::remove/$1/$2/$3',
|
||||
[
|
||||
'as' => 'episode-person-remove',
|
||||
'filter' =>
|
||||
'permission:podcast_episodes-edit',
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -497,6 +571,7 @@ $routes->group('@(:podcastName)', function ($routes) {
|
|||
$routes->head('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
||||
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
||||
});
|
||||
$routes->get('/credits', 'Page::credits', ['as' => 'credits']);
|
||||
$routes->get('/(:slug)', 'Page/$1', ['as' => 'page']);
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Controllers\Admin;
|
||||
|
||||
use App\Models\EpisodePersonModel;
|
||||
use App\Models\PodcastModel;
|
||||
use App\Models\EpisodeModel;
|
||||
use App\Models\PersonModel;
|
||||
|
||||
class EpisodePerson extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Podcast
|
||||
*/
|
||||
protected $podcast;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\Episode
|
||||
*/
|
||||
protected $episode;
|
||||
|
||||
public function _remap($method, ...$params)
|
||||
{
|
||||
if (count($params) > 1) {
|
||||
if (
|
||||
!($this->podcast = (new PodcastModel())->getPodcastById(
|
||||
$params[0]
|
||||
))
|
||||
) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
if (
|
||||
!($this->episode = (new EpisodeModel())
|
||||
->where([
|
||||
'id' => $params[1],
|
||||
'podcast_id' => $params[0],
|
||||
])
|
||||
->first())
|
||||
) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
} else {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
unset($params[1]);
|
||||
unset($params[0]);
|
||||
|
||||
return $this->$method(...$params);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
helper('form');
|
||||
|
||||
$data = [
|
||||
'episode' => $this->episode,
|
||||
'podcast' => $this->podcast,
|
||||
'episodePersons' => (new EpisodePersonModel())->getPersonsByEpisodeId(
|
||||
$this->podcast->id,
|
||||
$this->episode->id
|
||||
),
|
||||
'personOptions' => (new PersonModel())->getPersonOptions(),
|
||||
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
|
||||
];
|
||||
replace_breadcrumb_params([
|
||||
0 => $this->podcast->title,
|
||||
1 => $this->episode->title,
|
||||
]);
|
||||
return view('admin/episode/person', $data);
|
||||
}
|
||||
|
||||
public function attemptAdd()
|
||||
{
|
||||
$rules = [
|
||||
'person' => 'required',
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
||||
(new EpisodePersonModel())->addEpisodePersons(
|
||||
$this->podcast->id,
|
||||
$this->episode->id,
|
||||
$this->request->getPost('person'),
|
||||
$this->request->getPost('person_group_role')
|
||||
);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function remove($episodePersonId)
|
||||
{
|
||||
(new EpisodePersonModel())->removeEpisodePersons(
|
||||
$this->podcast->id,
|
||||
$this->episode->id,
|
||||
$episodePersonId
|
||||
);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Controllers\Admin;
|
||||
|
||||
use App\Models\PersonModel;
|
||||
|
||||
class Person extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Person|null
|
||||
*/
|
||||
protected $person;
|
||||
|
||||
public function _remap($method, ...$params)
|
||||
{
|
||||
if (count($params) > 0) {
|
||||
if (
|
||||
!($this->person = (new PersonModel())->getPersonById(
|
||||
$params[0]
|
||||
))
|
||||
) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->$method();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ['persons' => (new PersonModel())->findAll()];
|
||||
|
||||
return view('admin/person/list', $data);
|
||||
}
|
||||
|
||||
public function view()
|
||||
{
|
||||
$data = ['person' => $this->person];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->person->full_name]);
|
||||
return view('admin/person/view', $data);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
helper(['form']);
|
||||
|
||||
return view('admin/person/create');
|
||||
}
|
||||
|
||||
public function attemptCreate()
|
||||
{
|
||||
$rules = [
|
||||
'image' =>
|
||||
'is_image[image]|ext_in[image,jpg,jpeg,png]|min_dims[image,400,400]|is_image_squared[image]',
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
||||
$person = new \App\Entities\Person([
|
||||
'full_name' => $this->request->getPost('full_name'),
|
||||
'unique_name' => $this->request->getPost('unique_name'),
|
||||
'information_url' => $this->request->getPost('information_url'),
|
||||
'image' => $this->request->getFile('image'),
|
||||
'created_by' => user()->id,
|
||||
'updated_by' => user()->id,
|
||||
]);
|
||||
|
||||
$personModel = new PersonModel();
|
||||
|
||||
if (!$personModel->insert($person)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $personModel->errors());
|
||||
}
|
||||
|
||||
return redirect()->route('person-list');
|
||||
}
|
||||
|
||||
public function edit()
|
||||
{
|
||||
helper('form');
|
||||
|
||||
$data = [
|
||||
'person' => $this->person,
|
||||
];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->person->full_name]);
|
||||
return view('admin/person/edit', $data);
|
||||
}
|
||||
|
||||
public function attemptEdit()
|
||||
{
|
||||
$rules = [
|
||||
'image' =>
|
||||
'is_image[image]|ext_in[image,jpg,jpeg,png]|min_dims[image,400,400]|is_image_squared[image]',
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
||||
$this->person->full_name = $this->request->getPost('full_name');
|
||||
$this->person->unique_name = $this->request->getPost('unique_name');
|
||||
$this->person->information_url = $this->request->getPost(
|
||||
'information_url'
|
||||
);
|
||||
$image = $this->request->getFile('image');
|
||||
if ($image->isValid()) {
|
||||
$this->person->image = $image;
|
||||
}
|
||||
|
||||
$this->updated_by = user();
|
||||
|
||||
$personModel = new PersonModel();
|
||||
if (!$personModel->update($this->person->id, $this->person)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $personModel->errors());
|
||||
}
|
||||
|
||||
return redirect()->route('person-view', [$this->person->id]);
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
(new PersonModel())->delete($this->person->id);
|
||||
|
||||
return redirect()->route('person-list');
|
||||
}
|
||||
}
|
|
@ -13,6 +13,9 @@ use App\Models\LanguageModel;
|
|||
use App\Models\PodcastModel;
|
||||
use App\Models\EpisodeModel;
|
||||
use App\Models\PlatformModel;
|
||||
use App\Models\PersonModel;
|
||||
use App\Models\PodcastPersonModel;
|
||||
use App\Models\EpisodePersonModel;
|
||||
use Config\Services;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
|
||||
|
@ -150,7 +153,7 @@ class PodcastImport extends BaseController
|
|||
: $nsItunes->complete === 'yes',
|
||||
'location_name' => !$nsPodcast->location
|
||||
? null
|
||||
: $nsPodcast->location->attributes()['name'],
|
||||
: $nsPodcast->location,
|
||||
'location_geo' =>
|
||||
!$nsPodcast->location ||
|
||||
empty($nsPodcast->location->attributes()['geo'])
|
||||
|
@ -158,9 +161,9 @@ class PodcastImport extends BaseController
|
|||
: $nsPodcast->location->attributes()['geo'],
|
||||
'location_osmid' =>
|
||||
!$nsPodcast->location ||
|
||||
empty($nsPodcast->location->attributes()['osmid'])
|
||||
empty($nsPodcast->location->attributes()['osm'])
|
||||
? null
|
||||
: $nsPodcast->location->attributes()['osmid'],
|
||||
: $nsPodcast->location->attributes()['osm'],
|
||||
'created_by' => user(),
|
||||
'updated_by' => user(),
|
||||
]);
|
||||
|
@ -200,40 +203,40 @@ class PodcastImport extends BaseController
|
|||
$podcastAdminGroup->id
|
||||
);
|
||||
|
||||
$platformModel = new PlatformModel();
|
||||
$podcastsPlatformsData = [];
|
||||
foreach ($nsPodcast->id as $podcastingPlatform) {
|
||||
$slug = $podcastingPlatform->attributes()['platform'];
|
||||
$platformModel->getOrCreatePlatform($slug, 'podcasting');
|
||||
array_push($podcastsPlatformsData, [
|
||||
'platform_slug' => $slug,
|
||||
'podcast_id' => $newPodcastId,
|
||||
'link_url' => $podcastingPlatform->attributes()['url'],
|
||||
'link_content' => $podcastingPlatform->attributes()['id'],
|
||||
'is_visible' => false,
|
||||
]);
|
||||
}
|
||||
foreach ($nsPodcast->social as $socialPlatform) {
|
||||
$slug = $socialPlatform->attributes()['platform'];
|
||||
$platformModel->getOrCreatePlatform($slug, 'social');
|
||||
array_push($podcastsPlatformsData, [
|
||||
'platform_slug' => $socialPlatform->attributes()['platform'],
|
||||
'podcast_id' => $newPodcastId,
|
||||
'link_url' => $socialPlatform->attributes()['url'],
|
||||
'link_content' => $socialPlatform,
|
||||
'is_visible' => false,
|
||||
]);
|
||||
}
|
||||
foreach ($nsPodcast->funding as $fundingPlatform) {
|
||||
$slug = $fundingPlatform->attributes()['platform'];
|
||||
$platformModel->getOrCreatePlatform($slug, 'funding');
|
||||
array_push($podcastsPlatformsData, [
|
||||
'platform_slug' => $fundingPlatform->attributes()['platform'],
|
||||
'podcast_id' => $newPodcastId,
|
||||
'link_url' => $fundingPlatform->attributes()['url'],
|
||||
'link_content' => $fundingPlatform->attributes()['id'],
|
||||
'is_visible' => false,
|
||||
]);
|
||||
$platformTypes = [
|
||||
['name' => 'podcasting', 'elements' => $nsPodcast->id],
|
||||
['name' => 'social', 'elements' => $nsPodcast->social],
|
||||
['name' => 'funding', 'elements' => $nsPodcast->funding],
|
||||
];
|
||||
$platformModel = new PlatformModel();
|
||||
foreach ($platformTypes as $platformType) {
|
||||
foreach ($platformType['elements'] as $platform) {
|
||||
$platformLabel = $platform->attributes()['platform'];
|
||||
$platformSlug = slugify($platformLabel);
|
||||
if (!$platformModel->getPlatform($platformSlug)) {
|
||||
if (
|
||||
!$platformModel->createPlatform(
|
||||
$platformSlug,
|
||||
$platformType['name'],
|
||||
$platformLabel,
|
||||
''
|
||||
)
|
||||
) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $platformModel->errors());
|
||||
}
|
||||
}
|
||||
array_push($podcastsPlatformsData, [
|
||||
'platform_slug' => $platformSlug,
|
||||
'podcast_id' => $newPodcastId,
|
||||
'link_url' => $platform->attributes()['url'],
|
||||
'link_content' => $platform->attributes()['id'],
|
||||
'is_visible' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (count($podcastsPlatformsData) > 1) {
|
||||
$platformModel->createPodcastPlatforms(
|
||||
|
@ -242,6 +245,54 @@ class PodcastImport extends BaseController
|
|||
);
|
||||
}
|
||||
|
||||
foreach ($nsPodcast->person as $podcastPerson) {
|
||||
$personModel = new PersonModel();
|
||||
$newPersonId = null;
|
||||
if ($newPerson = $personModel->getPerson($podcastPerson)) {
|
||||
$newPersonId = $newPerson->id;
|
||||
} else {
|
||||
if (
|
||||
!($newPersonId = $personModel->createPerson(
|
||||
$podcastPerson,
|
||||
$podcastPerson->attributes()['href'],
|
||||
$podcastPerson->attributes()['img']
|
||||
))
|
||||
) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $personModel->errors());
|
||||
}
|
||||
}
|
||||
|
||||
$personGroup = empty($podcastPerson->attributes()['group'])
|
||||
? ['slug' => '']
|
||||
: \Podlibre\PodcastNamespace\ReversedTaxonomy::$taxonomy[
|
||||
(string) $podcastPerson->attributes()['group']
|
||||
];
|
||||
$personRole =
|
||||
empty($podcastPerson->attributes()['role']) ||
|
||||
empty($personGroup)
|
||||
? ['slug' => '']
|
||||
: $personGroup['roles'][
|
||||
strval($podcastPerson->attributes()['role'])
|
||||
];
|
||||
$newPodcastPerson = new \App\Entities\PodcastPerson([
|
||||
'podcast_id' => $newPodcastId,
|
||||
'person_id' => $newPersonId,
|
||||
'person_group' => $personGroup['slug'],
|
||||
'person_role' => $personRole['slug'],
|
||||
]);
|
||||
$podcastPersonModel = new PodcastPersonModel();
|
||||
|
||||
if (!$podcastPersonModel->insert($newPodcastPerson)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $podcastPersonModel->errors());
|
||||
}
|
||||
}
|
||||
|
||||
$numberItems = $feed->channel[0]->item->count();
|
||||
$lastItem =
|
||||
!empty($this->request->getPost('max_episodes')) &&
|
||||
|
@ -251,6 +302,7 @@ class PodcastImport extends BaseController
|
|||
|
||||
$slugs = [];
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// For each Episode:
|
||||
for ($itemNumber = 1; $itemNumber <= $lastItem; $itemNumber++) {
|
||||
$item = $feed->channel[0]->item[$numberItems - $itemNumber];
|
||||
|
@ -326,7 +378,7 @@ class PodcastImport extends BaseController
|
|||
: $nsItunes->block === 'yes',
|
||||
'location_name' => !$nsPodcast->location
|
||||
? null
|
||||
: $nsPodcast->location->attributes()['name'],
|
||||
: $nsPodcast->location,
|
||||
'location_geo' =>
|
||||
!$nsPodcast->location ||
|
||||
empty($nsPodcast->location->attributes()['geo'])
|
||||
|
@ -334,9 +386,9 @@ class PodcastImport extends BaseController
|
|||
: $nsPodcast->location->attributes()['geo'],
|
||||
'location_osmid' =>
|
||||
!$nsPodcast->location ||
|
||||
empty($nsPodcast->location->attributes()['osmid'])
|
||||
empty($nsPodcast->location->attributes()['osm'])
|
||||
? null
|
||||
: $nsPodcast->location->attributes()['osmid'],
|
||||
: $nsPodcast->location->attributes()['osm'],
|
||||
'created_by' => user(),
|
||||
'updated_by' => user(),
|
||||
'published_at' => strtotime($item->pubDate),
|
||||
|
@ -344,13 +396,62 @@ class PodcastImport extends BaseController
|
|||
|
||||
$episodeModel = new EpisodeModel();
|
||||
|
||||
if (!$episodeModel->insert($newEpisode)) {
|
||||
if (!($newEpisodeId = $episodeModel->insert($newEpisode, true))) {
|
||||
// FIXME: What shall we do?
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $episodeModel->errors());
|
||||
}
|
||||
|
||||
foreach ($nsPodcast->person as $episodePerson) {
|
||||
$personModel = new PersonModel();
|
||||
$newPersonId = null;
|
||||
if ($newPerson = $personModel->getPerson($episodePerson)) {
|
||||
$newPersonId = $newPerson->id;
|
||||
} else {
|
||||
if (
|
||||
!($newPersonId = $personModel->createPerson(
|
||||
$episodePerson,
|
||||
$episodePerson->attributes()['href'],
|
||||
$episodePerson->attributes()['img']
|
||||
))
|
||||
) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $personModel->errors());
|
||||
}
|
||||
}
|
||||
|
||||
$personGroup = empty($episodePerson->attributes()['group'])
|
||||
? ['slug' => '']
|
||||
: \Podlibre\PodcastNamespace\ReversedTaxonomy::$taxonomy[
|
||||
strval($episodePerson->attributes()['group'])
|
||||
];
|
||||
$personRole =
|
||||
empty($episodePerson->attributes()['role']) ||
|
||||
empty($personGroup)
|
||||
? ['slug' => '']
|
||||
: $personGroup['roles'][
|
||||
strval($episodePerson->attributes()['role'])
|
||||
];
|
||||
$newEpisodePerson = new \App\Entities\PodcastPerson([
|
||||
'podcast_id' => $newPodcastId,
|
||||
'episode_id' => $newEpisodeId,
|
||||
'person_id' => $newPersonId,
|
||||
'person_group' => $personGroup['slug'],
|
||||
'person_role' => $personRole['slug'],
|
||||
]);
|
||||
$episodePersonModel = new EpisodePersonModel();
|
||||
|
||||
if (!$episodePersonModel->insert($newEpisodePerson)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $episodePersonModel->errors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->transComplete();
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Controllers\Admin;
|
||||
|
||||
use App\Models\PodcastPersonModel;
|
||||
use App\Models\PodcastModel;
|
||||
use App\Models\PersonModel;
|
||||
|
||||
class PodcastPerson extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Podcast
|
||||
*/
|
||||
protected $podcast;
|
||||
|
||||
public function _remap($method, ...$params)
|
||||
{
|
||||
if (count($params) > 0) {
|
||||
if (
|
||||
!($this->podcast = (new PodcastModel())->getPodcastById(
|
||||
$params[0]
|
||||
))
|
||||
) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
} else {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
unset($params[0]);
|
||||
|
||||
return $this->$method(...$params);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
helper('form');
|
||||
|
||||
$data = [
|
||||
'podcast' => $this->podcast,
|
||||
'podcastPersons' => (new PodcastPersonModel())->getPersonsByPodcastId(
|
||||
$this->podcast->id
|
||||
),
|
||||
'personOptions' => (new PersonModel())->getPersonOptions(),
|
||||
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
|
||||
];
|
||||
replace_breadcrumb_params([
|
||||
0 => $this->podcast->title,
|
||||
]);
|
||||
return view('admin/podcast/person', $data);
|
||||
}
|
||||
|
||||
public function attemptAdd()
|
||||
{
|
||||
$rules = [
|
||||
'person' => 'required',
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
||||
(new PodcastPersonModel())->addPodcastPersons(
|
||||
$this->podcast->id,
|
||||
$this->request->getPost('person'),
|
||||
$this->request->getPost('person_group_role')
|
||||
);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function remove($podcastPersonId)
|
||||
{
|
||||
(new PodcastPersonModel())->removePodcastPersons(
|
||||
$this->podcast->id,
|
||||
$podcastPersonId
|
||||
);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
|
@ -54,11 +54,55 @@ class Episode extends BaseController
|
|||
$this->podcast->type
|
||||
);
|
||||
|
||||
$persons = [];
|
||||
foreach ($this->episode->episode_persons as $episodePerson) {
|
||||
if (array_key_exists($episodePerson->person->id, $persons)) {
|
||||
$persons[$episodePerson->person->id]['roles'] .=
|
||||
empty($episodePerson->person_group) ||
|
||||
empty($episodePerson->person_role)
|
||||
? ''
|
||||
: (empty(
|
||||
$persons[$episodePerson->person->id][
|
||||
'roles'
|
||||
]
|
||||
)
|
||||
? ''
|
||||
: ', ') .
|
||||
lang(
|
||||
'PersonsTaxonomy.persons.' .
|
||||
$episodePerson->person_group .
|
||||
'.roles.' .
|
||||
$episodePerson->person_role .
|
||||
'.label'
|
||||
);
|
||||
} else {
|
||||
$persons[$episodePerson->person->id] = [
|
||||
'full_name' => $episodePerson->person->full_name,
|
||||
'information_url' =>
|
||||
$episodePerson->person->information_url,
|
||||
'thumbnail_url' =>
|
||||
$episodePerson->person->image->thumbnail_url,
|
||||
'roles' =>
|
||||
empty($episodePerson->person_group) ||
|
||||
empty($episodePerson->person_role)
|
||||
? ''
|
||||
: lang(
|
||||
'PersonsTaxonomy.persons.' .
|
||||
$episodePerson->person_group .
|
||||
'.roles.' .
|
||||
$episodePerson->person_role .
|
||||
'.label'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'previousEpisode' => $previousNextEpisodes['previous'],
|
||||
'nextEpisode' => $previousNextEpisodes['next'],
|
||||
'podcast' => $this->podcast,
|
||||
'episode' => $this->episode,
|
||||
'persons' => $persons,
|
||||
];
|
||||
|
||||
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
namespace App\Controllers;
|
||||
|
||||
use App\Models\PageModel;
|
||||
use App\Models\CreditModel;
|
||||
use App\Models\PodcastModel;
|
||||
|
||||
class Page extends BaseController
|
||||
{
|
||||
|
@ -42,4 +44,137 @@ class Page extends BaseController
|
|||
];
|
||||
return view('page', $data);
|
||||
}
|
||||
|
||||
public function credits()
|
||||
{
|
||||
$locale = service('request')->getLocale();
|
||||
$model = new PodcastModel();
|
||||
$allPodcasts = $model->findAll();
|
||||
|
||||
if (!($found = cache("credits_{$locale}"))) {
|
||||
$page = new \App\Entities\Page([
|
||||
'title' => lang('Person.credits', [], $locale),
|
||||
'slug' => 'credits',
|
||||
'content' => '',
|
||||
]);
|
||||
|
||||
$creditModel = (new CreditModel())->findAll();
|
||||
|
||||
// Unlike the carpenter, we make a tree from a table:
|
||||
|
||||
$person_group = null;
|
||||
$person_id = null;
|
||||
$person_role = null;
|
||||
$credits = [];
|
||||
foreach ($creditModel as $credit) {
|
||||
if ($person_group !== $credit->person_group) {
|
||||
$person_group = $credit->person_group;
|
||||
$person_id = $credit->person_id;
|
||||
$person_role = $credit->person_role;
|
||||
$credits[$person_group] = [
|
||||
'group_label' => $credit->group_label,
|
||||
'persons' => [
|
||||
$person_id => [
|
||||
'full_name' => $credit->person->full_name,
|
||||
'thumbnail_url' =>
|
||||
$credit->person->image->thumbnail_url,
|
||||
'information_url' =>
|
||||
$credit->person->information_url,
|
||||
'roles' => [
|
||||
$person_role => [
|
||||
'role_label' => $credit->role_label,
|
||||
'is_in' => [
|
||||
[
|
||||
'link' => $credit->episode
|
||||
? $credit->episode->link
|
||||
: $credit->podcast->link,
|
||||
'title' => $credit->episode
|
||||
? (count($allPodcasts) > 1
|
||||
? "{$credit->podcast->title} ▸ "
|
||||
: '') .
|
||||
"(S{$credit->episode->season_number}E{$credit->episode->number}) {$credit->episode->title}"
|
||||
: $credit->podcast->title,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
} elseif ($person_id !== $credit->person_id) {
|
||||
$person_id = $credit->person_id;
|
||||
$person_role = $credit->person_role;
|
||||
$credits[$person_group]['persons'][$person_id] = [
|
||||
'full_name' => $credit->person->full_name,
|
||||
'thumbnail_url' =>
|
||||
$credit->person->image->thumbnail_url,
|
||||
'information_url' => $credit->person->information_url,
|
||||
'roles' => [
|
||||
$person_role => [
|
||||
'role_label' => $credit->role_label,
|
||||
'is_in' => [
|
||||
[
|
||||
'link' => $credit->episode
|
||||
? $credit->episode->link
|
||||
: $credit->podcast->link,
|
||||
'title' => $credit->episode
|
||||
? (count($allPodcasts) > 1
|
||||
? "{$credit->podcast->title} ▸ "
|
||||
: '') .
|
||||
"(S{$credit->episode->season_number}E{$credit->episode->number}) {$credit->episode->title}"
|
||||
: $credit->podcast->title,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
} elseif ($person_role !== $credit->person_role) {
|
||||
$person_role = $credit->person_role;
|
||||
$credits[$person_group]['persons'][$person_id]['roles'][
|
||||
$person_role
|
||||
] = [
|
||||
'role_label' => $credit->role_label,
|
||||
'is_in' => [
|
||||
[
|
||||
'link' => $credit->episode
|
||||
? $credit->episode->link
|
||||
: $credit->podcast->link,
|
||||
'title' => $credit->episode
|
||||
? (count($allPodcasts) > 1
|
||||
? "{$credit->podcast->title} ▸ "
|
||||
: '') .
|
||||
"(S{$credit->episode->season_number}E{$credit->episode->number}) {$credit->episode->title}"
|
||||
: $credit->podcast->title,
|
||||
],
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$credits[$person_group]['persons'][$person_id]['roles'][
|
||||
$person_role
|
||||
]['is_in'][] = [
|
||||
'link' => $credit->episode
|
||||
? $credit->episode->link
|
||||
: $credit->podcast->link,
|
||||
'title' => $credit->episode
|
||||
? (count($allPodcasts) > 1
|
||||
? "{$credit->podcast->title} ▸ "
|
||||
: '') .
|
||||
"(S{$credit->episode->season_number}E{$credit->episode->number}) {$credit->episode->title}"
|
||||
: $credit->podcast->title,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'page' => $page,
|
||||
'credits' => $credits,
|
||||
];
|
||||
|
||||
$found = view('credits', $data);
|
||||
|
||||
cache()->save("credits_{$locale}", $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,49 @@ class Podcast extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
$persons = [];
|
||||
foreach ($this->podcast->podcast_persons as $podcastPerson) {
|
||||
if (array_key_exists($podcastPerson->person->id, $persons)) {
|
||||
$persons[$podcastPerson->person->id]['roles'] .=
|
||||
empty($podcastPerson->person_group) ||
|
||||
empty($podcastPerson->person_role)
|
||||
? ''
|
||||
: (empty(
|
||||
$persons[$podcastPerson->person->id][
|
||||
'roles'
|
||||
]
|
||||
)
|
||||
? ''
|
||||
: ', ') .
|
||||
lang(
|
||||
'PersonsTaxonomy.persons.' .
|
||||
$podcastPerson->person_group .
|
||||
'.roles.' .
|
||||
$podcastPerson->person_role .
|
||||
'.label'
|
||||
);
|
||||
} else {
|
||||
$persons[$podcastPerson->person->id] = [
|
||||
'full_name' => $podcastPerson->person->full_name,
|
||||
'information_url' =>
|
||||
$podcastPerson->person->information_url,
|
||||
'thumbnail_url' =>
|
||||
$podcastPerson->person->image->thumbnail_url,
|
||||
'roles' =>
|
||||
empty($podcastPerson->person_group) ||
|
||||
empty($podcastPerson->person_role)
|
||||
? ''
|
||||
: lang(
|
||||
'PersonsTaxonomy.persons.' .
|
||||
$podcastPerson->person_group .
|
||||
'.roles.' .
|
||||
$podcastPerson->person_role .
|
||||
'.label'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'podcast' => $this->podcast,
|
||||
'episodesNav' => $episodesNavigation,
|
||||
|
@ -119,6 +162,7 @@ class Podcast extends BaseController
|
|||
$yearQuery,
|
||||
$seasonQuery
|
||||
),
|
||||
'personArray' => $persons,
|
||||
];
|
||||
|
||||
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
|
||||
|
|
|
@ -41,11 +41,9 @@ class AddPlatforms extends Migration
|
|||
'default' => null,
|
||||
],
|
||||
]);
|
||||
$this->forge->addField('`created_at` timestamp NOT NULL DEFAULT NOW()');
|
||||
$this->forge->addField(
|
||||
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()'
|
||||
);
|
||||
$this->forge->addField(
|
||||
'`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()'
|
||||
'`updated_at` timestamp NOT NULL DEFAULT NOW() ON UPDATE NOW()'
|
||||
);
|
||||
$this->forge->addKey('slug', true);
|
||||
$this->forge->createTable('platforms');
|
||||
|
|
|
@ -40,12 +40,6 @@ class AddPodcastsPlatforms extends Migration
|
|||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
'created_at' => [
|
||||
'type' => 'DATETIME',
|
||||
],
|
||||
'updated_at' => [
|
||||
'type' => 'DATETIME',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->forge->addPrimaryKey(['podcast_id', 'platform_slug']);
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class Persons
|
||||
* Creates persons table in database
|
||||
*
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddPersons extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->forge->addField([
|
||||
'id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
'auto_increment' => true,
|
||||
],
|
||||
'full_name' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 192,
|
||||
'comment' => 'This is the full name or alias of the person.',
|
||||
],
|
||||
'unique_name' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 192,
|
||||
'comment' => 'This is the slug name or alias of the person.',
|
||||
'unique' => true,
|
||||
],
|
||||
'information_url' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 512,
|
||||
'comment' =>
|
||||
'The url to a relevant resource of information about the person, such as a homepage or third-party profile platform.',
|
||||
'null' => true,
|
||||
],
|
||||
'image_uri' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
],
|
||||
'created_by' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'updated_by' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'created_at' => [
|
||||
'type' => 'DATETIME',
|
||||
],
|
||||
'updated_at' => [
|
||||
'type' => 'DATETIME',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->forge->addKey('id', true);
|
||||
$this->forge->addForeignKey('created_by', 'users', 'id');
|
||||
$this->forge->addForeignKey('updated_by', 'users', 'id');
|
||||
$this->forge->createTable('persons');
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->forge->dropTable('persons');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class AddPodcastsPersons
|
||||
* Creates podcasts_persons table in database
|
||||
*
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddPodcastsPersons extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->forge->addField([
|
||||
'id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
'auto_increment' => true,
|
||||
],
|
||||
'podcast_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'person_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'person_group' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
'person_role' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
]);
|
||||
$this->forge->addKey('id', true);
|
||||
$this->forge->addUniqueKey([
|
||||
'podcast_id',
|
||||
'person_id',
|
||||
'person_group',
|
||||
'person_role',
|
||||
]);
|
||||
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id');
|
||||
$this->forge->addForeignKey('person_id', 'persons', 'id');
|
||||
$this->forge->createTable('podcasts_persons');
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->forge->dropTable('podcasts_persons');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class AddEpisodesPersons
|
||||
* Creates episodes_persons table in database
|
||||
*
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddEpisodesPersons extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->forge->addField([
|
||||
'id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
'auto_increment' => true,
|
||||
],
|
||||
'podcast_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'episode_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'person_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'person_group' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
'person_role' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
]);
|
||||
$this->forge->addKey('id', true);
|
||||
$this->forge->addUniqueKey([
|
||||
'podcast_id',
|
||||
'episode_id',
|
||||
'person_id',
|
||||
'person_group',
|
||||
'person_role',
|
||||
]);
|
||||
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id');
|
||||
$this->forge->addForeignKey('episode_id', 'episodes', 'id');
|
||||
$this->forge->addForeignKey('person_id', 'persons', 'id');
|
||||
$this->forge->createTable('episodes_persons');
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->forge->dropTable('episodes_persons');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class AddCreditView
|
||||
* Creates Credit View in database
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddCreditView extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
// Creates View for credit UNION query
|
||||
$viewName = $this->db->prefixTable('credits');
|
||||
$personTable = $this->db->prefixTable('persons');
|
||||
$podcastPersonTable = $this->db->prefixTable('podcasts_persons');
|
||||
$episodePersonTable = $this->db->prefixTable('episodes_persons');
|
||||
$createQuery = <<<EOD
|
||||
CREATE VIEW `$viewName` AS
|
||||
SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `$podcastPersonTable`
|
||||
INNER JOIN `$personTable`
|
||||
ON (`person_id`=`$personTable`.`id`)
|
||||
UNION
|
||||
SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, `episode_id` FROM `$episodePersonTable`
|
||||
INNER JOIN `$personTable`
|
||||
ON (`person_id`=`$personTable`.`id`)
|
||||
ORDER BY `person_group`, `full_name`, `person_role`, `podcast_id`, `episode_id`;
|
||||
EOD;
|
||||
$this->db->query($createQuery);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$viewName = $this->db->prefixTable('credits');
|
||||
$this->db->query("DROP VIEW IF EXISTS `$viewName`");
|
||||
}
|
||||
}
|
|
@ -198,6 +198,33 @@ class AuthSeeder extends Seeder
|
|||
'has_permission' => ['podcast_admin'],
|
||||
],
|
||||
],
|
||||
'person' => [
|
||||
[
|
||||
'name' => 'create',
|
||||
'description' => 'Add a new person',
|
||||
'has_permission' => ['superadmin'],
|
||||
],
|
||||
[
|
||||
'name' => 'list',
|
||||
'description' => 'List all persons',
|
||||
'has_permission' => ['superadmin'],
|
||||
],
|
||||
[
|
||||
'name' => 'view',
|
||||
'description' => 'View any person',
|
||||
'has_permission' => ['superadmin'],
|
||||
],
|
||||
[
|
||||
'name' => 'edit',
|
||||
'description' => 'Edit a person',
|
||||
'has_permission' => ['superadmin'],
|
||||
],
|
||||
[
|
||||
'name' => 'delete_permanently',
|
||||
'description' => 'Delete any person from the database',
|
||||
'has_permission' => ['superadmin'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
static function getGroupIdByName($name, $dataGroups)
|
||||
|
|
|
@ -47,6 +47,13 @@ class PlatformSeeder extends Seeder
|
|||
'home_url' => 'https://www.blubrry.com/',
|
||||
'submit_url' => 'https://www.blubrry.com/addpodcast.php',
|
||||
],
|
||||
[
|
||||
'slug' => 'breaker',
|
||||
'type' => 'podcasting',
|
||||
'label' => 'Breaker',
|
||||
'home_url' => 'https://www.breaker.audio/',
|
||||
'submit_url' => 'https://podcasters.breaker.audio/',
|
||||
],
|
||||
[
|
||||
'slug' => 'castbox',
|
||||
'type' => 'podcasting',
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use App\Models\PersonModel;
|
||||
use App\Models\PodcastModel;
|
||||
use App\Models\EpisodeModel;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
class Credit extends Entity
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Person
|
||||
*/
|
||||
protected $person;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\Podcast
|
||||
*/
|
||||
protected $podcast;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\Episode
|
||||
*/
|
||||
protected $episode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $group_label;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $role_label;
|
||||
|
||||
public function getPodcast()
|
||||
{
|
||||
return (new PodcastModel())->getPodcastById(
|
||||
$this->attributes['podcast_id']
|
||||
);
|
||||
}
|
||||
|
||||
public function getEpisode()
|
||||
{
|
||||
if (empty($this->attributes['episode_id'])) {
|
||||
return null;
|
||||
} else {
|
||||
return (new EpisodeModel())->getEpisodeById(
|
||||
$this->attributes['podcast_id'],
|
||||
$this->attributes['episode_id']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getPerson()
|
||||
{
|
||||
return (new PersonModel())->getPersonById(
|
||||
$this->attributes['person_id']
|
||||
);
|
||||
}
|
||||
|
||||
public function getGroupLabel()
|
||||
{
|
||||
if (empty($this->attributes['person_group'])) {
|
||||
return null;
|
||||
} else {
|
||||
return lang(
|
||||
"PersonsTaxonomy.persons.{$this->attributes['person_group']}.label"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getRoleLabel()
|
||||
{
|
||||
if (
|
||||
empty($this->attributes['person_group']) ||
|
||||
empty($this->attributes['person_role'])
|
||||
) {
|
||||
return null;
|
||||
} else {
|
||||
return lang(
|
||||
"PersonsTaxonomy.persons.{$this->attributes['person_group']}.roles.{$this->attributes['person_role']}.label"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace App\Entities;
|
|||
|
||||
use App\Models\PodcastModel;
|
||||
use App\Models\SoundbiteModel;
|
||||
use App\Models\EpisodePersonModel;
|
||||
use CodeIgniter\Entity;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
|
@ -76,6 +77,11 @@ class Episode extends Entity
|
|||
*/
|
||||
protected $chapters_url;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\EpisodePerson[]
|
||||
*/
|
||||
protected $episode_persons;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\Soundbite[]
|
||||
*/
|
||||
|
@ -358,6 +364,29 @@ class Episode extends Entity
|
|||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the episode's persons
|
||||
*
|
||||
* @return \App\Entities\EpisodePerson[]
|
||||
*/
|
||||
public function getEpisodePersons()
|
||||
{
|
||||
if (empty($this->id)) {
|
||||
throw new \RuntimeException(
|
||||
'Episode must be created before getting persons.'
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($this->episode_persons)) {
|
||||
$this->episode_persons = (new EpisodePersonModel())->getPersonsByEpisodeId(
|
||||
$this->podcast_id,
|
||||
$this->id
|
||||
);
|
||||
}
|
||||
|
||||
return $this->episode_persons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the episode’s soundbites
|
||||
*
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
use App\Models\PersonModel;
|
||||
|
||||
class EpisodePerson extends Entity
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Person
|
||||
*/
|
||||
protected $person;
|
||||
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'podcast_id' => 'integer',
|
||||
'episode_id' => 'integer',
|
||||
'person_id' => 'integer',
|
||||
'person_group' => '?string',
|
||||
'person_role' => '?string',
|
||||
];
|
||||
|
||||
public function getPerson()
|
||||
{
|
||||
return (new PersonModel())->getPersonById(
|
||||
$this->attributes['person_id']
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
class Person extends Entity
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Image
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'full_name' => 'string',
|
||||
'unique_name' => 'string',
|
||||
'information_url' => '?string',
|
||||
'image_uri' => 'string',
|
||||
'created_by' => 'integer',
|
||||
'updated_by' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Saves a picture in `public/media/~person/`
|
||||
*
|
||||
* @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image
|
||||
*
|
||||
*/
|
||||
public function setImage($image = null)
|
||||
{
|
||||
if ($image) {
|
||||
helper('media');
|
||||
|
||||
$this->attributes['image_uri'] = save_podcast_media(
|
||||
$image,
|
||||
'~person',
|
||||
$this->attributes['unique_name']
|
||||
);
|
||||
$this->image = new \App\Entities\Image(
|
||||
$this->attributes['image_uri']
|
||||
);
|
||||
$this->image->saveSizes();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImage()
|
||||
{
|
||||
return new \App\Entities\Image($this->attributes['image_uri']);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ namespace App\Entities;
|
|||
use App\Models\CategoryModel;
|
||||
use App\Models\EpisodeModel;
|
||||
use App\Models\PlatformModel;
|
||||
use App\Models\PodcastPersonModel;
|
||||
use CodeIgniter\Entity;
|
||||
use App\Models\UserModel;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
|
@ -32,6 +33,11 @@ class Podcast extends Entity
|
|||
*/
|
||||
protected $episodes;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\PodcastPerson[]
|
||||
*/
|
||||
protected $podcast_persons;
|
||||
|
||||
/**
|
||||
* @var \App\Entities\Category
|
||||
*/
|
||||
|
@ -167,6 +173,28 @@ class Podcast extends Entity
|
|||
return $this->episodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the podcast's persons
|
||||
*
|
||||
* @return \App\Entities\PodcastPerson[]
|
||||
*/
|
||||
public function getPodcastPersons()
|
||||
{
|
||||
if (empty($this->id)) {
|
||||
throw new \RuntimeException(
|
||||
'Podcast must be created before getting persons.'
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($this->podcast_persons)) {
|
||||
$this->podcast_persons = (new PodcastPersonModel())->getPersonsByPodcastId(
|
||||
$this->id
|
||||
);
|
||||
}
|
||||
|
||||
return $this->podcast_persons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the podcast category entity
|
||||
*
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
use App\Models\PersonModel;
|
||||
|
||||
class PodcastPerson extends Entity
|
||||
{
|
||||
/**
|
||||
* @var \App\Entities\Person
|
||||
*/
|
||||
protected $person;
|
||||
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'podcast_id' => 'integer',
|
||||
'person_id' => 'integer',
|
||||
'person_group' => '?string',
|
||||
'person_role' => '?string',
|
||||
];
|
||||
|
||||
public function getPerson()
|
||||
{
|
||||
return (new PersonModel())->getPersonById(
|
||||
$this->attributes['person_id']
|
||||
);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@ function render_page_links($class = null)
|
|||
$links = anchor(route_to('home'), lang('Common.home'), [
|
||||
'class' => 'px-2 underline hover:no-underline',
|
||||
]);
|
||||
$links .= anchor(route_to('credits'), lang('Person.credits'), [
|
||||
'class' => 'px-2 underline hover:no-underline',
|
||||
]);
|
||||
foreach ($pages as $page) {
|
||||
$links .= anchor($page->link, $page->title, [
|
||||
'class' => 'px-2 underline hover:no-underline',
|
||||
|
|
|
@ -68,18 +68,14 @@ function get_rss_feed($podcast, $serviceSlug = '')
|
|||
if (!empty($podcast->location_name)) {
|
||||
$locationElement = $channel->addChild(
|
||||
'location',
|
||||
null,
|
||||
htmlspecialchars($podcast->location_name),
|
||||
$podcast_namespace
|
||||
);
|
||||
$locationElement->addAttribute(
|
||||
'name',
|
||||
htmlspecialchars($podcast->location_name)
|
||||
);
|
||||
if (!empty($podcast->location_geo)) {
|
||||
$locationElement->addAttribute('geo', $podcast->location_geo);
|
||||
}
|
||||
if (!empty($podcast->location_osmid)) {
|
||||
$locationElement->addAttribute('osmid', $podcast->location_osmid);
|
||||
$locationElement->addAttribute('osm', $podcast->location_osmid);
|
||||
}
|
||||
}
|
||||
if (!empty($podcast->payment_pointer)) {
|
||||
|
@ -105,7 +101,7 @@ function get_rss_feed($podcast, $serviceSlug = '')
|
|||
)
|
||||
->addAttribute('owner', $podcast->owner_email);
|
||||
if (!empty($podcast->imported_feed_url)) {
|
||||
$channel->addChildWithCDATA(
|
||||
$channel->addChild(
|
||||
'previousUrl',
|
||||
$podcast->imported_feed_url,
|
||||
$podcast_namespace
|
||||
|
@ -169,6 +165,51 @@ function get_rss_feed($podcast, $serviceSlug = '')
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($podcast->podcast_persons as $podcastPerson) {
|
||||
$podcastPersonElement = $channel->addChild(
|
||||
'person',
|
||||
htmlspecialchars($podcastPerson->person->full_name),
|
||||
$podcast_namespace
|
||||
);
|
||||
if (
|
||||
!empty($podcastPerson->person_role) &&
|
||||
!empty($podcastPerson->person_group)
|
||||
) {
|
||||
$podcastPersonElement->addAttribute(
|
||||
'role',
|
||||
htmlspecialchars(
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label",
|
||||
[],
|
||||
'en'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!empty($podcastPerson->person_group)) {
|
||||
$podcastPersonElement->addAttribute(
|
||||
'group',
|
||||
htmlspecialchars(
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$podcastPerson->person_group}.label",
|
||||
[],
|
||||
'en'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
$podcastPersonElement->addAttribute(
|
||||
'img',
|
||||
$podcastPerson->person->image->large_url
|
||||
);
|
||||
if (!empty($podcastPerson->person->information_url)) {
|
||||
$podcastPersonElement->addAttribute(
|
||||
'href',
|
||||
$podcastPerson->person->information_url
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// set main category first, then other categories as apple
|
||||
add_category_tag($channel, $podcast->category);
|
||||
foreach ($podcast->other_categories as $other_category) {
|
||||
|
@ -222,21 +263,14 @@ function get_rss_feed($podcast, $serviceSlug = '')
|
|||
if (!empty($episode->location_name)) {
|
||||
$locationElement = $item->addChild(
|
||||
'location',
|
||||
null,
|
||||
htmlspecialchars($episode->location_name),
|
||||
$podcast_namespace
|
||||
);
|
||||
$locationElement->addAttribute(
|
||||
'name',
|
||||
htmlspecialchars($episode->location_name)
|
||||
);
|
||||
if (!empty($episode->location_geo)) {
|
||||
$locationElement->addAttribute('geo', $episode->location_geo);
|
||||
}
|
||||
if (!empty($episode->location_osmid)) {
|
||||
$locationElement->addAttribute(
|
||||
'osmid',
|
||||
$episode->location_osmid
|
||||
);
|
||||
$locationElement->addAttribute('osm', $episode->location_osmid);
|
||||
}
|
||||
}
|
||||
$item->addChildWithCDATA('description', $episode->description_html);
|
||||
|
@ -312,6 +346,51 @@ function get_rss_feed($podcast, $serviceSlug = '')
|
|||
$soundbiteElement->addAttribute('duration', $soundbite->duration);
|
||||
}
|
||||
|
||||
foreach ($episode->episode_persons as $episodePerson) {
|
||||
$episodePersonElement = $item->addChild(
|
||||
'person',
|
||||
htmlspecialchars($episodePerson->person->full_name),
|
||||
$podcast_namespace
|
||||
);
|
||||
if (
|
||||
!empty($episodePerson->person_role) &&
|
||||
!empty($episodePerson->person_group)
|
||||
) {
|
||||
$episodePersonElement->addAttribute(
|
||||
'role',
|
||||
htmlspecialchars(
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label",
|
||||
[],
|
||||
'en'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!empty($episodePerson->person_group)) {
|
||||
$episodePersonElement->addAttribute(
|
||||
'group',
|
||||
htmlspecialchars(
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$episodePerson->person_group}.label",
|
||||
[],
|
||||
'en'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
$episodePersonElement->addAttribute(
|
||||
'img',
|
||||
$episodePerson->person->image->large_url
|
||||
);
|
||||
if (!empty($episodePerson->person->information_url)) {
|
||||
$episodePersonElement->addAttribute(
|
||||
'href',
|
||||
$episodePerson->person->information_url
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$episode->is_blocked &&
|
||||
$item->addChild('block', 'Yes', $itunes_namespace);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ return [
|
|||
'podcast-list' => 'All podcasts',
|
||||
'podcast-create' => 'New podcast',
|
||||
'podcast-import' => 'Import a podcast',
|
||||
'persons' => 'Persons',
|
||||
'person-list' => 'All persons',
|
||||
'person-create' => 'New person',
|
||||
'users' => 'Users',
|
||||
'user-list' => 'All users',
|
||||
'user-create' => 'New user',
|
||||
|
|
|
@ -16,6 +16,7 @@ return [
|
|||
'add' => 'add',
|
||||
'new' => 'new',
|
||||
'edit' => 'edit',
|
||||
'persons' => 'persons',
|
||||
'users' => 'users',
|
||||
'my-account' => 'my account',
|
||||
'change-password' => 'change password',
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
return [
|
||||
'persons' => 'Persons',
|
||||
'all_persons' => 'All persons',
|
||||
'no_person' => 'Nobody found!',
|
||||
'create' => 'Create a person',
|
||||
'view' => 'View person',
|
||||
'edit' => 'Edit person',
|
||||
'delete' => 'Delete person',
|
||||
'form' => [
|
||||
'identity_section_title' => 'Identity',
|
||||
'identity_section_subtitle' => 'Who is working on the podcast',
|
||||
'full_name' => 'Full name',
|
||||
'full_name_hint' => 'This is the full name or alias of the person.',
|
||||
'unique_name' => 'Unique name',
|
||||
'unique_name_hint' => 'Used for URLs',
|
||||
'information_url' => 'Information URL',
|
||||
'information_url_hint' =>
|
||||
'Url to a relevant resource of information about the person, such as a homepage or third-party profile platform.',
|
||||
'image' => 'Picture, avatar, image',
|
||||
'image_size_hint' =>
|
||||
'Image must be squared with at least 400px wide and tall.',
|
||||
'submit_create' => 'Create person',
|
||||
'submit_edit' => 'Save person',
|
||||
],
|
||||
'podcast_form' => [
|
||||
'title' => 'Manage persons',
|
||||
'manage_section_title' => 'Management',
|
||||
'manage_section_subtitle' => 'Remove persons from this podcast',
|
||||
'add_section_title' => 'Add persons to this podcast',
|
||||
'add_section_subtitle' => 'You may pick several persons and roles.',
|
||||
'person' => 'Persons',
|
||||
'person_hint' =>
|
||||
'You may select one or several persons with the same roles. You need to create the persons first.',
|
||||
'group_role' => 'Groups and roles',
|
||||
'group_role_hint' =>
|
||||
'You may select none, one or several groups and roles for a person.',
|
||||
'submit_add' => 'Add person(s)',
|
||||
'remove' => 'Remove',
|
||||
],
|
||||
'episode_form' => [
|
||||
'title' => 'Manage persons',
|
||||
'manage_section_title' => 'Management',
|
||||
'manage_section_subtitle' => 'Remove persons from this episode',
|
||||
'add_section_title' => 'Add persons to this episode',
|
||||
'add_section_subtitle' => 'You may pick several persons and roles',
|
||||
'person' => 'Persons',
|
||||
'person_hint' =>
|
||||
'You may select one or several persons with the same roles. You need to create the persons first.',
|
||||
'group_role' => 'Groups and roles',
|
||||
'group_role_hint' =>
|
||||
'You may select none, one or several groups and roles for a person.',
|
||||
'submit_add' => 'Add person(s)',
|
||||
'remove' => 'Remove',
|
||||
],
|
||||
'credits' => 'Credits',
|
||||
];
|
|
@ -27,7 +27,8 @@ return [
|
|||
'image' => 'Cover image',
|
||||
'title' => 'Title',
|
||||
'name' => 'Name',
|
||||
'name_hint' => 'Used for generating the podcast URL.',
|
||||
'name_hint' =>
|
||||
'Used for generating the podcast URL. Uppercase, lowercase, numbers and underscores are accepted.',
|
||||
'type' => [
|
||||
'label' => 'Type',
|
||||
'hint' =>
|
||||
|
|
|
@ -15,6 +15,8 @@ return [
|
|||
'episode-list' => 'All episodes',
|
||||
'episode-create' => 'New episode',
|
||||
'analytics' => 'Analytics',
|
||||
'persons' => 'Persons',
|
||||
'podcast-person-manage' => 'Manage persons',
|
||||
'contributors' => 'Contributors',
|
||||
'contributor-list' => 'All contributors',
|
||||
'contributor-add' => 'Add contributor',
|
||||
|
|
|
@ -14,6 +14,9 @@ return [
|
|||
'podcast-list' => 'Tous les podcasts',
|
||||
'podcast-create' => 'Créer un podcast',
|
||||
'podcast-import' => 'Importer un podcast',
|
||||
'persons' => 'Intervenants',
|
||||
'person-list' => 'Tous les intervenants',
|
||||
'person-create' => 'Nouvel intervenant',
|
||||
'users' => 'Utilisateurs',
|
||||
'user-list' => 'Tous les utilisateurs',
|
||||
'user-create' => 'Créer un utilisateur',
|
||||
|
|
|
@ -16,6 +16,7 @@ return [
|
|||
'add' => 'ajouter',
|
||||
'new' => 'créer',
|
||||
'edit' => 'modifier',
|
||||
'persons' => 'intervenants',
|
||||
'users' => 'utilisateurs',
|
||||
'my-account' => 'mon compte',
|
||||
'change-password' => 'changer le mot de passe',
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
return [
|
||||
'persons' => 'Intervenants',
|
||||
'all_persons' => 'Tous les intervenants',
|
||||
'no_person' => 'Aucun intervenant trouvé !',
|
||||
'create' => 'Créer un intervenant',
|
||||
'view' => 'Voir l’intervenant',
|
||||
'edit' => 'Modifier l’intervenant',
|
||||
'delete' => 'Supprimer l’intervenant',
|
||||
'form' => [
|
||||
'identity_section_title' => 'Identité',
|
||||
'identity_section_subtitle' => 'Qui intervient sur le podcast',
|
||||
'full_name' => 'Nom complet',
|
||||
'full_name_hint' => 'Le nom complet ou le pseudonyme de l’intervenant',
|
||||
'unique_name' => 'Nom unique',
|
||||
'unique_name_hint' => 'Utilisé pour les URLs',
|
||||
'information_url' => 'Adresse d’information',
|
||||
'information_url_hint' =>
|
||||
'URL pointant vers des informations relatives à l’intervenant, telle qu’une page personnelle ou une page de profil sur une plateforme tierce.',
|
||||
'image' => 'Photo, avatar, image',
|
||||
'image_size_hint' =>
|
||||
'L’image doit être carrée et avoir au moins 400px de largeur et de hauteur.',
|
||||
'submit_create' => 'Créer l’intervenant',
|
||||
'submit_edit' => 'Enregistrer l’intervenant',
|
||||
],
|
||||
'podcast_form' => [
|
||||
'title' => 'Gérer les intervenants',
|
||||
'manage_section_title' => 'Gestion',
|
||||
'manage_section_subtitle' => 'Retirer des intervenants de ce podcast',
|
||||
'add_section_title' => 'Ajouter des intervenants à ce podcast',
|
||||
'add_section_subtitle' =>
|
||||
'Vous pouvez sélectionner plusieurs intervenants et rôles.',
|
||||
'person' => 'Intervenants',
|
||||
'person_hint' =>
|
||||
'Vous pouvez selectionner un ou plusieurs intervenants ayant les mêmes rôles. Les intervenants doivent avoir été préalablement créés.',
|
||||
'group_role' => 'Groupes et rôles',
|
||||
'group_role_hint' =>
|
||||
'Vous pouvez sélectionner aucun, un ou plusieurs groupes et rôles par intervenant.',
|
||||
'submit_add' => 'Ajouter un/des intervenant(s)',
|
||||
'remove' => 'Retirer',
|
||||
],
|
||||
'episode_form' => [
|
||||
'title' => 'Gérer les intervenants',
|
||||
'manage_section_title' => 'Gestion',
|
||||
'manage_section_subtitle' => 'Retirer des intervenants de cet épisode',
|
||||
'add_section_title' => 'Ajouter des intervenants à cet épisode',
|
||||
'add_section_subtitle' =>
|
||||
'Vous pouvez sélectionner plusieurs intervenants et rôles.',
|
||||
'person' => 'Intervenants',
|
||||
'person_hint' =>
|
||||
'Vous pouvez selectionner un ou plusieurs intervenants ayant les mêmes rôles. Les intervenants doivent avoir été préalablement créés.',
|
||||
'group_role' => 'Groupes et rôles',
|
||||
'group_role_hint' =>
|
||||
'Vous pouvez sélectionner aucun, un ou plusieurs groupes et rôles par intervenant.',
|
||||
'submit_add' => 'Ajouter un/des intervenant(s)',
|
||||
'remove' => 'Retirer',
|
||||
],
|
||||
'credits' => 'Crédits',
|
||||
];
|
|
@ -28,7 +28,8 @@ return [
|
|||
'image' => 'Image de couverture',
|
||||
'title' => 'Titre',
|
||||
'name' => 'Nom',
|
||||
'name_hint' => 'Utilisé pour l’adresse du podcast.',
|
||||
'name_hint' =>
|
||||
'Utilisé pour l’adresse du podcast. Les majuscules, les minuscules, les chiffres et le caractère souligné « _ » sont acceptés.',
|
||||
'type' => [
|
||||
'label' => 'Type',
|
||||
'hint' =>
|
||||
|
|
|
@ -15,6 +15,8 @@ return [
|
|||
'episode-list' => 'Tous les épisodes',
|
||||
'episode-create' => 'Créer un épisode',
|
||||
'analytics' => 'Mesures d’audience',
|
||||
'persons' => 'Intervenants',
|
||||
'podcast-person-manage' => 'Gestion des intervenants',
|
||||
'contributors' => 'Contributeurs',
|
||||
'contributor-list' => 'Tous les contributeurs',
|
||||
'contributor-add' => 'Ajouter un contributeur',
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class CreditModel extends Model
|
||||
{
|
||||
protected $table = 'credits';
|
||||
|
||||
protected $allowedFields = [];
|
||||
|
||||
protected $returnType = \App\Entities\Credit::class;
|
||||
}
|
|
@ -89,6 +89,26 @@ class EpisodeModel extends Model
|
|||
return $found;
|
||||
}
|
||||
|
||||
public function getEpisodeById($podcastId, $episodeId)
|
||||
{
|
||||
if (!($found = cache("podcast{$podcastId}_episode{$episodeId}"))) {
|
||||
$found = $this->where([
|
||||
'podcast_id' => $podcastId,
|
||||
'id' => $episodeId,
|
||||
])
|
||||
->where('published_at <=', 'NOW()')
|
||||
->first();
|
||||
|
||||
cache()->save(
|
||||
"podcast{$podcastId}_episode{$episodeId}",
|
||||
$found,
|
||||
DECADE
|
||||
);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous episode based on episode ordering
|
||||
*/
|
||||
|
@ -334,7 +354,7 @@ class EpisodeModel extends Model
|
|||
return $data;
|
||||
}
|
||||
|
||||
protected function clearCache(array $data)
|
||||
public function clearCache(array $data)
|
||||
{
|
||||
$episodeModel = new EpisodeModel();
|
||||
$episode = (new EpisodeModel())->find(
|
||||
|
@ -366,6 +386,7 @@ class EpisodeModel extends Model
|
|||
cache()->delete(
|
||||
"page_podcast{$episode->podcast->id}_episode{$episode->id}_{$locale}"
|
||||
);
|
||||
cache()->delete("credits_{$locale}");
|
||||
}
|
||||
|
||||
foreach ($years as $year) {
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class EpisodePersonModel extends Model
|
||||
{
|
||||
protected $table = 'episodes_persons';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $allowedFields = [
|
||||
'id',
|
||||
'podcast_id',
|
||||
'episode_id',
|
||||
'person_id',
|
||||
'person_group',
|
||||
'person_role',
|
||||
];
|
||||
|
||||
protected $returnType = \App\Entities\EpisodePerson::class;
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
protected $useTimestamps = false;
|
||||
|
||||
protected $validationRules = [
|
||||
'episode_id' => 'required',
|
||||
'person_id' => 'required',
|
||||
];
|
||||
protected $validationMessages = [];
|
||||
|
||||
protected $afterInsert = ['clearCache'];
|
||||
protected $beforeDelete = ['clearCache'];
|
||||
|
||||
public function getPersonsByEpisodeId($podcastId, $episodeId)
|
||||
{
|
||||
if (
|
||||
!($found = cache(
|
||||
"podcast{$podcastId}_episodes{$episodeId}_persons"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('episodes_persons.*')
|
||||
->where('episode_id', $episodeId)
|
||||
->join(
|
||||
'persons',
|
||||
'person_id=persons.id'
|
||||
)
|
||||
->orderby('full_name')
|
||||
->findAll();
|
||||
|
||||
cache()->save(
|
||||
"podcast{$podcastId}_episodes{$episodeId}_persons",
|
||||
$found,
|
||||
DECADE
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add persons to episode
|
||||
*
|
||||
* @param int podcastId
|
||||
* @param int $episodeId
|
||||
* @param array $persons
|
||||
* @param array $groups_roles
|
||||
*
|
||||
* @return integer|false Number of rows inserted or FALSE on failure
|
||||
*/
|
||||
public function addEpisodePersons(
|
||||
$podcastId,
|
||||
$episodeId,
|
||||
$persons,
|
||||
$groups_roles
|
||||
) {
|
||||
if (!empty($persons)) {
|
||||
$this->clearCache([
|
||||
'id' => [
|
||||
'podcast_id' => $podcastId,
|
||||
'episode_id' => $episodeId,
|
||||
],
|
||||
]);
|
||||
$data = [];
|
||||
foreach ($persons as $person) {
|
||||
if ($groups_roles) {
|
||||
foreach ($groups_roles as $group_role) {
|
||||
$group_role = explode(',', $group_role);
|
||||
$data[] = [
|
||||
'podcast_id' => $podcastId,
|
||||
'episode_id' => $episodeId,
|
||||
'person_id' => $person,
|
||||
'person_group' => $group_role[0],
|
||||
'person_role' => $group_role[1],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$data[] = [
|
||||
'podcast_id' => $podcastId,
|
||||
'episode_id' => $episodeId,
|
||||
'person_id' => $person,
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->insertBatch($data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function removeEpisodePersons(
|
||||
$podcastId,
|
||||
$episodeId,
|
||||
$episodePersonId
|
||||
) {
|
||||
return $this->delete([
|
||||
'podcast_id' => $podcastId,
|
||||
'episode_id' => $episodeId,
|
||||
'id' => $episodePersonId,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function clearCache(array $data)
|
||||
{
|
||||
$podcastId = null;
|
||||
$episodeId = null;
|
||||
if (
|
||||
isset($data['id']['podcast_id']) &&
|
||||
isset($data['id']['episode_id'])
|
||||
) {
|
||||
$podcastId = $data['id']['podcast_id'];
|
||||
$episodeId = $data['id']['episode_id'];
|
||||
} else {
|
||||
$episodePerson = (new EpisodePersonModel())->find(
|
||||
is_array($data['id']) ? $data['id']['id'] : $data['id']
|
||||
);
|
||||
$podcastId = $episodePerson->podcast_id;
|
||||
$episodeId = $episodePerson->episode_id;
|
||||
}
|
||||
|
||||
cache()->delete("podcast{$podcastId}_episodes{$episodeId}_persons");
|
||||
(new EpisodeModel())->clearCache(['id' => $episodeId]);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class PersonModel extends Model
|
||||
{
|
||||
protected $table = 'persons';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $allowedFields = [
|
||||
'id',
|
||||
'full_name',
|
||||
'unique_name',
|
||||
'information_url',
|
||||
'image_uri',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
protected $returnType = \App\Entities\Person::class;
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
protected $useTimestamps = true;
|
||||
|
||||
protected $validationRules = [
|
||||
'full_name' => 'required',
|
||||
'unique_name' =>
|
||||
'required|regex_match[/^[a-z0-9\-]{1,191}$/]|is_unique[persons.unique_name,id,{id}]',
|
||||
'image_uri' => 'required',
|
||||
'created_by' => 'required',
|
||||
'updated_by' => 'required',
|
||||
];
|
||||
protected $validationMessages = [];
|
||||
|
||||
// clear cache before update if by any chance, the person name changes, so will the person link
|
||||
protected $afterInsert = ['clearCache'];
|
||||
protected $beforeUpdate = ['clearCache'];
|
||||
protected $beforeDelete = ['clearCache'];
|
||||
|
||||
public function getPersonById($personId)
|
||||
{
|
||||
if (!($found = cache("person{$personId}"))) {
|
||||
$found = $this->find($personId);
|
||||
cache()->save("person{$personId}", $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
public function getPerson($fullName)
|
||||
{
|
||||
return $this->where('full_name', $fullName)->first();
|
||||
}
|
||||
|
||||
public function createPerson($fullName, $informationUrl, $image)
|
||||
{
|
||||
$person = new \App\Entities\Person([
|
||||
'full_name' => $fullName,
|
||||
'unique_name' => slugify($fullName),
|
||||
'information_url' => $informationUrl,
|
||||
'image' => download_file($image),
|
||||
'created_by' => user()->id,
|
||||
'updated_by' => user()->id,
|
||||
]);
|
||||
return $this->insert($person);
|
||||
}
|
||||
|
||||
public function getPersonOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
if (!($options = cache('person_options'))) {
|
||||
$options = array_reduce(
|
||||
$this->select('`id`, `full_name`')
|
||||
->orderBy('`full_name`', 'ASC')
|
||||
->findAll(),
|
||||
function ($result, $person) {
|
||||
$result[$person->id] = $person->full_name;
|
||||
return $result;
|
||||
},
|
||||
[]
|
||||
);
|
||||
cache()->save('person_options', $options, DECADE);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getTaxonomyOptions()
|
||||
{
|
||||
$options = [];
|
||||
$locale = service('request')->getLocale();
|
||||
if (!($options = cache("taxonomy_options_{$locale}"))) {
|
||||
foreach (lang('PersonsTaxonomy.persons') as $group_key => $group) {
|
||||
foreach ($group['roles'] as $role_key => $role) {
|
||||
$options[
|
||||
"$group_key,$role_key"
|
||||
] = "{$group['label']} ▸ {$role['label']}";
|
||||
}
|
||||
}
|
||||
|
||||
cache()->save("taxonomy_options_{$locale}", $options, DECADE);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function clearCache(array $data)
|
||||
{
|
||||
$person = (new PersonModel())->getPersonById(
|
||||
is_array($data['id']) ? $data['id'][0] : $data['id']
|
||||
);
|
||||
|
||||
cache()->delete('person_options');
|
||||
cache()->delete("person{$person->id}");
|
||||
cache()->delete("user{$person->created_by}_persons");
|
||||
|
||||
$supportedLocales = config('App')->supportedLocales;
|
||||
// clear cache for every credit page
|
||||
foreach ($supportedLocales as $locale) {
|
||||
cache()->delete("credit_{$locale}");
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -16,14 +16,20 @@ use CodeIgniter\Model;
|
|||
class PlatformModel extends Model
|
||||
{
|
||||
protected $table = 'platforms';
|
||||
protected $primaryKey = 'id';
|
||||
protected $primaryKey = 'slug';
|
||||
|
||||
protected $allowedFields = ['slug', 'label', 'home_url', 'submit_url'];
|
||||
protected $allowedFields = [
|
||||
'slug',
|
||||
'type',
|
||||
'label',
|
||||
'home_url',
|
||||
'submit_url',
|
||||
];
|
||||
|
||||
protected $returnType = \App\Entities\Platform::class;
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
protected $useTimestamps = true;
|
||||
protected $useTimestamps = false;
|
||||
|
||||
public function getPlatforms()
|
||||
{
|
||||
|
@ -37,26 +43,32 @@ class PlatformModel extends Model
|
|||
return $found;
|
||||
}
|
||||
|
||||
public function getOrCreatePlatform($slug, $platformType)
|
||||
public function getPlatform($slug)
|
||||
{
|
||||
if (!($found = cache("platforms_$slug"))) {
|
||||
if (!($found = cache("platform_$slug"))) {
|
||||
$found = $this->where('slug', $slug)->first();
|
||||
if (!$found) {
|
||||
$data = [
|
||||
'slug' => $slug,
|
||||
'type' => $platformType,
|
||||
'label' => $slug,
|
||||
'home_url' => '',
|
||||
'submit_url' => null,
|
||||
];
|
||||
$this->insert($data);
|
||||
$found = $this->where('slug', $slug)->first();
|
||||
}
|
||||
cache()->save("platforms_$slug", $found, DECADE);
|
||||
cache()->save("platform_$slug", $found, DECADE);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
public function createPlatform(
|
||||
$slug,
|
||||
$type,
|
||||
$label,
|
||||
$homeUrl,
|
||||
$submitUrl = null
|
||||
) {
|
||||
$data = [
|
||||
'slug' => $slug,
|
||||
'type' => $type,
|
||||
'label' => $label,
|
||||
'home_url' => $homeUrl,
|
||||
'submit_url' => $submitUrl,
|
||||
];
|
||||
return $this->insert($data, false);
|
||||
}
|
||||
|
||||
public function getPlatformsWithLinks($podcastId, $platformType)
|
||||
{
|
||||
if (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
@ -170,7 +170,7 @@ class PodcastModel extends Model
|
|||
: false;
|
||||
}
|
||||
|
||||
protected function clearCache(array $data)
|
||||
public function clearCache(array $data)
|
||||
{
|
||||
$podcast = (new PodcastModel())->getPodcastById(
|
||||
is_array($data['id']) ? $data['id'][0] : $data['id']
|
||||
|
@ -195,6 +195,10 @@ class PodcastModel extends Model
|
|||
);
|
||||
}
|
||||
}
|
||||
// clear cache for every credit page
|
||||
foreach ($supportedLocales as $locale) {
|
||||
cache()->delete("credits_{$locale}");
|
||||
}
|
||||
|
||||
// delete episode lists cache per year / season
|
||||
// and localized pages
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class PodcastPersonModel extends Model
|
||||
{
|
||||
protected $table = 'podcasts_persons';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $allowedFields = [
|
||||
'id',
|
||||
'podcast_id',
|
||||
'person_id',
|
||||
'person_group',
|
||||
'person_role',
|
||||
];
|
||||
|
||||
protected $returnType = \App\Entities\PodcastPerson::class;
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
protected $useTimestamps = false;
|
||||
|
||||
protected $validationRules = [
|
||||
'podcast_id' => 'required',
|
||||
'person_id' => 'required',
|
||||
];
|
||||
protected $validationMessages = [];
|
||||
|
||||
protected $afterInsert = ['clearCache'];
|
||||
protected $beforeDelete = ['clearCache'];
|
||||
|
||||
public function getPersonsByPodcastId($podcastId)
|
||||
{
|
||||
if (!($found = cache("podcast{$podcastId}_persons"))) {
|
||||
$found = $this->select('podcasts_persons.*')
|
||||
->where('podcast_id', $podcastId)
|
||||
->join(
|
||||
'persons',
|
||||
'person_id=persons.id'
|
||||
)
|
||||
->orderby('full_name')
|
||||
->findAll();
|
||||
|
||||
cache()->save("podcast{$podcastId}_persons", $found, DECADE);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add persons to podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
* @param array $persons
|
||||
* @param array $groups_roles
|
||||
*
|
||||
* @return integer Number of rows inserted or FALSE on failure
|
||||
*/
|
||||
public function addPodcastPersons($podcastId, $persons, $groups_roles)
|
||||
{
|
||||
if (!empty($persons)) {
|
||||
$this->clearCache(['id' => ['podcast_id' => $podcastId]]);
|
||||
$data = [];
|
||||
foreach ($persons as $person) {
|
||||
if ($groups_roles) {
|
||||
foreach ($groups_roles as $group_role) {
|
||||
$group_role = explode(',', $group_role);
|
||||
$data[] = [
|
||||
'podcast_id' => $podcastId,
|
||||
'person_id' => $person,
|
||||
'person_group' => $group_role[0],
|
||||
'person_role' => $group_role[1],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$data[] = [
|
||||
'podcast_id' => $podcastId,
|
||||
'person_id' => $person,
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->insertBatch($data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function removePodcastPersons($podcastId, $podcastPersonId)
|
||||
{
|
||||
return $this->delete([
|
||||
'podcast_id' => $podcastId,
|
||||
'id' => $podcastPersonId,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function clearCache(array $data)
|
||||
{
|
||||
$podcastId = null;
|
||||
if (isset($data['id']['podcast_id'])) {
|
||||
$podcastId = $data['id']['podcast_id'];
|
||||
} else {
|
||||
$person = (new PodcastPersonModel())->find(
|
||||
is_array($data['id']) ? $data['id']['id'] : $data['id']
|
||||
);
|
||||
$podcastId = $person->podcast_id;
|
||||
}
|
||||
|
||||
cache()->delete("podcast{$podcastId}_persons");
|
||||
(new PodcastModel())->clearCache(['id' => $podcastId]);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12.414 5H21a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2zM4 5v14h16V7h-8.414l-2-2H4zm4 13a4 4 0 1 1 8 0H8zm4-5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z"/></svg>
|
After Width: | Height: | Size: 314 B |
|
@ -0,0 +1,11 @@
|
|||
<svg version="1.1" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="300" height="300" rx="67" fill="#f2f8ff"/>
|
||||
<g transform="matrix(1.36 0 0 1.36 -45.282 22.882)">
|
||||
<path d="m133.88 120.47c4.08 12.49 10.64 23.85 19.12 33.5 15.88-2.06 29.85-10.22 39.46-22.06-7.84 2.07-16.07 3.17-24.56 3.17-6.79 0-13.42-.72-19.81-2.06-6.67-1.36-12.07-6.21-14.21-12.55z" fill="#1269ff"/>
|
||||
<path d="m145.09 154.47c2.68 0 5.32-.17 7.91-.51-8.48-9.65-15.04-21.01-19.12-33.5-.64-1.91-.99-3.95-.99-6.07 0-4.77 1.76-9.12 4.66-12.46-11.1 12.41-19.02 27.71-22.48 44.64 8.85 5.03 19.1 7.9 30.02 7.9z" fill="#5c9dff"/>
|
||||
<path d="m85.78 107.8c4 16.61 14.79 30.57 29.28 38.78 3.47-16.95 11.4-32.28 22.52-44.69 3.48-3.98 8.6-6.49 14.3-6.49 1.27 0 2.5.12 3.7.36-6.36-1.33-12.95-2.04-19.71-2.04-18.35-.01-35.5 5.14-50.09 14.08z" fill="#9ec6ff"/>
|
||||
<path d="m155.59 95.754c-6.36-1.33-12.95-2.04-19.71-2.04-18.36 0-35.51 5.15-50.1 14.09-1.1-4.59-1.69-9.39-1.69-14.33 0-19.02 8.71-36.01 22.35-47.2 29.91 9.03 53.64 32.31 63.39 61.91.09.26.18.52.26.79-.08-.26-.17-.53-.26-.79-2.19-6.29-7.6-11.12-14.24-12.43z" fill="#d1e3ff"/>
|
||||
<path d="m203.08 82.474c0-27-22.49-50-58-50-14.66 0-28.12 5.18-38.64 13.8 30.18 9.12 54.07 32.7 63.64 62.69.56 1.75 1.07 3.62 1.53 5.42 17.34-.38 31.47-14.48 31.47-31.91z" fill="#fff"/>
|
||||
<path d="m151.89 98.394c1.12 0 2.06.09 3.12.3 5.56 1.1 10.12 5.14 11.98 10.43.54 1.7 1.25 4.25 1.7 6l.59 2.31 2.38-.05c18.94-.4 34.43-15.81 34.43-34.91 0-28.93-24.13-53-61-53-15.21.05-29.63 5.58-40.55 14.48-14.31 11.73-23.45 29.56-23.45 49.52.01 5.12.63 10.27 1.78 15.03 4.2 17.43 15.52 32.08 30.71 40.68 9.19 5.18 20.18 8.25 31.51 8.28 2.78 0 5.61-.19 8.29-.53 16.67-2.16 31.32-10.73 41.41-23.14l.66-.81-1.28-4.63-2.47.65c-7.66 2.03-15.51 3.08-23.8 3.07-6.63 0-12.95-.68-19.19-2-5.62-1.13-10.19-5.22-11.99-10.56-.58-1.68-.84-3.24-.83-5.11-.06-4.17 1.37-7.6 3.95-10.53 2.94-3.36 7.25-5.48 12.05-5.48zm-6.8-62.92c34.12 0 55 21.92 55 47 0 14.99-11.56 27.3-26.23 28.78-.39-1.39-.81-2.86-1.17-3.98-9.29-29.1-31.41-51.63-59.87-62.03 9.25-6.28 20.16-9.81 32.27-9.77zm-58 58c0-17.51 7.76-33.21 20.03-43.85 23.34 7.48 42.47 23.45 53.64 44.64-1.45-.64-2.98-1.13-4.58-1.45-6.5-1.35-13.39-2.1-20.3-2.1-17.28.03-33.84 4.58-48 12.4-.53-3.13-.79-6.31-.79-9.64zm60.39 42.48c6.53 1.37 13.47 2.12 20.42 2.12 5.31 0 10.59-.44 15.72-1.25-8.1 7.21-18.23 12.17-29.43 13.94-4.26-5-7.96-10.4-11.05-16.17 1.38.59 2.83 1.05 4.34 1.36zm-.45 15.49c-.64.02-1.29.03-1.94.03-9.73.03-18.59-2.26-26.63-6.45 2.31-10.17 6.25-19.6 11.58-28.14.18 1.57.53 3.13.99 4.52 3.57 10.89 9.08 21.14 16 30.04zm-11.69-51.54c-10.42 11.68-18.29 26.2-22.27 41.94-11.43-7.58-20.01-19.1-23.82-32.62 13.79-8.04 29.5-12.54 46.63-12.51.96 0 1.91.01 2.86.04-1.24.93-2.38 1.98-3.4 3.15z" fill="#003dad"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -11,8 +11,8 @@
|
|||
<link rel="stylesheet" href="/assets/index.css"/>
|
||||
</head>
|
||||
|
||||
<body class="flex flex-col min-h-screen mx-auto">
|
||||
<header class="border-b">
|
||||
<body class="flex flex-col min-h-screen mx-auto bg-gray-100">
|
||||
<header class="bg-white border-b">
|
||||
<div class="container flex items-center justify-between px-2 py-4 mx-auto">
|
||||
<a href="<?= route_to('home') ?>" class="text-2xl"><?= isset($page)
|
||||
? $page->title
|
||||
|
@ -22,11 +22,15 @@
|
|||
<main class="container flex-1 px-4 py-10 mx-auto">
|
||||
<?= $this->renderSection('content') ?>
|
||||
</main>
|
||||
<footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t">
|
||||
<?= render_page_links() ?>
|
||||
<small><?= lang('Common.powered_by', [
|
||||
'castopod' =>
|
||||
'<a class="underline hover:no-underline" href="https://castopod.org/" target="_blank" rel="noreferrer noopener">Castopod</a>',
|
||||
]) ?></small>
|
||||
</footer>
|
||||
<footer class="px-2 py-4 bg-white border-t">
|
||||
<div class="container flex flex-col items-center justify-between mx-auto text-xs md:flex-row ">
|
||||
<?= render_page_links('inline-flex mb-4 md:mb-0') ?>
|
||||
<p class="flex flex-col items-center md:items-end">
|
||||
<?= lang('Common.powered_by', [
|
||||
'castopod' =>
|
||||
'<a class="underline hover:no-underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod</a>',
|
||||
]) ?>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
@ -5,6 +5,10 @@ $navigation = [
|
|||
'icon' => 'mic',
|
||||
'items' => ['podcast-list', 'podcast-create', 'podcast-import'],
|
||||
],
|
||||
'persons' => [
|
||||
'icon' => 'folder-user',
|
||||
'items' => ['person-list', 'person-create'],
|
||||
],
|
||||
'users' => ['icon' => 'group', 'items' => ['user-list', 'user-create']],
|
||||
'pages' => ['icon' => 'pages', 'items' => ['page-list', 'page-create']],
|
||||
]; ?>
|
||||
|
|
|
@ -61,6 +61,11 @@
|
|||
$podcast->id,
|
||||
$episode->id
|
||||
) ?>"><?= lang('Episode.edit') ?></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(
|
||||
'soundbites-edit',
|
||||
$podcast->id,
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Person.episode_form.title') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= lang('Person.episode_form.title') ?> (<?= count($episodePersons) ?>)
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('headerRight') ?>
|
||||
<?= button(
|
||||
lang('Person.create'),
|
||||
route_to('person-create'),
|
||||
['variant' => 'primary', 'iconLeft' => 'add'],
|
||||
['class' => 'mr-2']
|
||||
) ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<?= form_open(route_to('episode-person-edit', $episode->id), [
|
||||
'method' => 'post',
|
||||
'class' => 'flex flex-col',
|
||||
]) ?>
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<?php if ($episodePersons): ?>
|
||||
|
||||
<?= form_section(
|
||||
lang('Person.episode_form.manage_section_title'),
|
||||
lang('Person.episode_form.manage_section_subtitle')
|
||||
) ?>
|
||||
|
||||
|
||||
<?= data_table(
|
||||
[
|
||||
[
|
||||
'header' => lang('Person.episode_form.person'),
|
||||
'cell' => function ($episodePerson) {
|
||||
return '<div class="flex">' .
|
||||
'<a href="' .
|
||||
route_to('person-view', $episodePerson->person->id) .
|
||||
"\"><img src=\"{$episodePerson->person->image->thumbnail_url}\" alt=\"{$episodePerson->person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" .
|
||||
'<div class="flex flex-col ml-3">' .
|
||||
$episodePerson->person->full_name .
|
||||
($episodePerson->person_group && $episodePerson->person_role
|
||||
? '<span class="text-sm text-gray-600">' .
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$episodePerson->person_group}.label"
|
||||
) .
|
||||
' ▸ ' .
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label"
|
||||
) .
|
||||
'</span>'
|
||||
: '') .
|
||||
(empty($episodePerson->person->information_url)
|
||||
? ''
|
||||
: "<a href=\"{$episodePerson->person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" .
|
||||
$episodePerson->person->information_url .
|
||||
'</a>') .
|
||||
'</div></div>';
|
||||
},
|
||||
],
|
||||
[
|
||||
'header' => lang('Common.actions'),
|
||||
'cell' => function ($episodePerson) {
|
||||
return button(
|
||||
lang('Person.episode_form.remove'),
|
||||
route_to(
|
||||
'episode-person-remove',
|
||||
$episodePerson->podcast_id,
|
||||
$episodePerson->episode_id,
|
||||
$episodePerson->id
|
||||
),
|
||||
['variant' => 'danger', 'size' => 'small']
|
||||
);
|
||||
},
|
||||
],
|
||||
],
|
||||
$episodePersons
|
||||
) ?>
|
||||
|
||||
<?= form_section_close() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<?= form_section(
|
||||
lang('Person.episode_form.add_section_title'),
|
||||
lang('Person.episode_form.add_section_subtitle')
|
||||
) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.episode_form.person'),
|
||||
'person',
|
||||
[],
|
||||
lang('Person.episode_form.person_hint')
|
||||
) ?>
|
||||
<?= form_multiselect('person[]', $personOptions, old('person', []), [
|
||||
'id' => 'person',
|
||||
'class' => 'form-select mb-4',
|
||||
'required' => 'required',
|
||||
]) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.episode_form.group_role'),
|
||||
'group_role',
|
||||
[],
|
||||
|
||||
lang('Person.episode_form.group_role_hint'),
|
||||
true
|
||||
) ?>
|
||||
<?= form_multiselect(
|
||||
'person_group_role[]',
|
||||
$taxonomyOptions,
|
||||
old('person_group_role', []),
|
||||
['id' => 'person_group_role', 'class' => 'form-select mb-4']
|
||||
) ?>
|
||||
|
||||
|
||||
<?= form_section_close() ?>
|
||||
<?= button(
|
||||
lang('Person.episode_form.submit_add'),
|
||||
null,
|
||||
['variant' => 'primary'],
|
||||
['type' => 'submit', 'class' => 'self-end']
|
||||
) ?>
|
||||
<?= form_close() ?>
|
||||
|
||||
<?= $this->endSection() ?>
|
|
@ -64,6 +64,12 @@
|
|||
['variant' => 'info', 'iconLeft' => 'edit'],
|
||||
['class' => 'mb-4']
|
||||
) ?>
|
||||
<?= button(
|
||||
lang('Person.episode_form.title'),
|
||||
route_to('episode-person-manage', $podcast->id, $episode->id),
|
||||
['variant' => 'info', 'iconLeft' => 'folder-user'],
|
||||
['class' => 'mb-4']
|
||||
) ?>
|
||||
<?php if (count($episode->soundbites) > 0): ?>
|
||||
<?= data_table(
|
||||
[
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Person.create') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= lang('Person.create') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<?= form_open_multipart(route_to('person-create'), [
|
||||
'method' => 'post',
|
||||
'class' => 'flex flex-col',
|
||||
]) ?>
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<?= form_section(
|
||||
lang('Person.form.identity_section_title'),
|
||||
lang('Person.form.identity_section_subtitle')
|
||||
) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.form.full_name'),
|
||||
'full_name',
|
||||
[],
|
||||
lang('Person.form.full_name_hint')
|
||||
) ?>
|
||||
<?= form_input([
|
||||
'id' => 'full_name',
|
||||
'name' => 'full_name',
|
||||
'class' => 'form-input mb-4',
|
||||
'value' => old('full_name'),
|
||||
'required' => 'required',
|
||||
'data-slugify' => 'title',
|
||||
]) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.form.unique_name'),
|
||||
'unique_name',
|
||||
[],
|
||||
lang('Person.form.unique_name_hint')
|
||||
) ?>
|
||||
<?= form_input([
|
||||
'id' => 'unique_name',
|
||||
'name' => 'unique_name',
|
||||
'class' => 'form-input mb-4',
|
||||
'value' => old('unique_name'),
|
||||
'required' => 'required',
|
||||
'data-slugify' => 'slug',
|
||||
]) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.form.information_url'),
|
||||
'information_url',
|
||||
[],
|
||||
lang('Person.form.information_url_hint'),
|
||||
true
|
||||
) ?>
|
||||
<?= form_input([
|
||||
'id' => 'information_url',
|
||||
'name' => 'information_url',
|
||||
'class' => 'form-input mb-4',
|
||||
'value' => old('information_url'),
|
||||
]) ?>
|
||||
|
||||
<?= form_label(lang('Person.form.image'), 'image') ?>
|
||||
<?= form_input([
|
||||
'id' => 'image',
|
||||
'name' => 'image',
|
||||
'class' => 'form-input',
|
||||
'required' => 'required',
|
||||
'type' => 'file',
|
||||
'accept' => '.jpg,.jpeg,.png',
|
||||
]) ?>
|
||||
<small class="mb-4 text-gray-600"><?= lang(
|
||||
'Person.form.image_size_hint'
|
||||
) ?></small>
|
||||
|
||||
<?= form_section_close() ?>
|
||||
|
||||
<?= button(
|
||||
lang('Person.form.submit_create'),
|
||||
null,
|
||||
['variant' => 'primary'],
|
||||
['type' => 'submit', 'class' => 'self-end']
|
||||
) ?>
|
||||
|
||||
|
||||
<?= form_close() ?>
|
||||
|
||||
|
||||
<?= $this->endSection() ?>
|
|
@ -0,0 +1,95 @@
|
|||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Person.edit') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= lang('Person.edit') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<?= form_open_multipart(route_to('person-edit', $person->id), [
|
||||
'method' => 'post',
|
||||
'class' => 'flex flex-col',
|
||||
]) ?>
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<?= form_section(
|
||||
lang('Person.form.identity_section_title'),
|
||||
lang('Person.form.identity_section_subtitle') .
|
||||
"<img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-32 h-32 mt-3 rounded\" />"
|
||||
) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.form.full_name'),
|
||||
'full_name',
|
||||
[],
|
||||
lang('Person.form.full_name_hint')
|
||||
) ?>
|
||||
<?= form_input([
|
||||
'id' => 'full_name',
|
||||
'name' => 'full_name',
|
||||
'class' => 'form-input mb-4',
|
||||
'value' => old('full_name', $person->full_name),
|
||||
'required' => 'required',
|
||||
'data-slugify' => 'title',
|
||||
]) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.form.unique_name'),
|
||||
'unique_name',
|
||||
[],
|
||||
lang('Person.form.unique_name_hint')
|
||||
) ?>
|
||||
<?= form_input([
|
||||
'id' => 'unique_name',
|
||||
'name' => 'unique_name',
|
||||
'class' => 'form-input mb-4',
|
||||
'value' => old('unique_name', $person->unique_name),
|
||||
'required' => 'required',
|
||||
'data-slugify' => 'slug',
|
||||
]) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.form.information_url'),
|
||||
'information_url',
|
||||
[],
|
||||
lang('Person.form.information_url_hint'),
|
||||
true
|
||||
) ?>
|
||||
<?= form_input([
|
||||
'id' => 'information_url',
|
||||
'name' => 'information_url',
|
||||
'class' => 'form-input mb-4',
|
||||
'value' => old('information_url', $person->information_url),
|
||||
]) ?>
|
||||
|
||||
<?= form_label(lang('Person.form.image'), 'image') ?>
|
||||
<?= form_input([
|
||||
'id' => 'image',
|
||||
'name' => 'image',
|
||||
'class' => 'form-input',
|
||||
'type' => 'file',
|
||||
'accept' => '.jpg,.jpeg,.png',
|
||||
]) ?>
|
||||
<small class="mb-4 text-gray-600"><?= lang(
|
||||
'Person.form.image_size_hint'
|
||||
) ?></small>
|
||||
|
||||
<?= form_section_close() ?>
|
||||
|
||||
<?= button(
|
||||
lang('Person.form.submit_edit'),
|
||||
null,
|
||||
['variant' => 'primary'],
|
||||
['type' => 'submit', 'class' => 'self-end']
|
||||
) ?>
|
||||
|
||||
|
||||
<?= form_close() ?>
|
||||
|
||||
|
||||
<?= $this->endSection() ?>
|
|
@ -0,0 +1,65 @@
|
|||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Person.all_persons') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= lang('Person.all_persons') ?> (<?= count($persons) ?>)
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('headerRight') ?>
|
||||
<?= button(
|
||||
lang('Person.create'),
|
||||
route_to('person-create'),
|
||||
['variant' => 'primary', 'iconLeft' => 'add'],
|
||||
['class' => 'mr-2']
|
||||
) ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<?php if (!empty($persons)): ?>
|
||||
<?php foreach ($persons as $person): ?>
|
||||
<article class="w-48 h-full mb-4 mr-4 overflow-hidden bg-white border rounded shadow">
|
||||
<img
|
||||
alt="<?= $person->full_name ?>"
|
||||
src="<?= $person->image
|
||||
->thumbnail_url ?>" class="object-cover w-40 w-full" />
|
||||
<div class="p-2">
|
||||
<a href="<?= route_to(
|
||||
'person-view',
|
||||
$person->id
|
||||
) ?>" class="hover:underline">
|
||||
<h2 class="font-semibold"><?= $person->full_name ?></h2>
|
||||
</a>
|
||||
</div>
|
||||
<footer class="flex items-center justify-end p-2">
|
||||
<a class="inline-flex p-2 mr-2 text-teal-700 bg-teal-100 rounded-full shadow-xs hover:bg-teal-200" href="<?= route_to(
|
||||
'person-edit',
|
||||
$person->id
|
||||
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
|
||||
'Person.edit'
|
||||
) ?>"><?= icon('edit') ?></a>
|
||||
<a class="inline-flex p-2 mr-2 text-gray-700 bg-red-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
|
||||
'person-delete',
|
||||
$person->id
|
||||
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
|
||||
'Person.delete'
|
||||
) ?>"><?= icon('delete-bin') ?></a>
|
||||
<a class="inline-flex p-2 text-gray-700 bg-gray-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
|
||||
'person-view',
|
||||
$person->id
|
||||
) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
|
||||
'Person.view'
|
||||
) ?>"><?= icon('eye') ?></a>
|
||||
</footer>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p class="italic"><?= lang('Person.no_person') ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
|
@ -0,0 +1,38 @@
|
|||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $person->full_name ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $person->full_name ?>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('headerRight') ?>
|
||||
<?= button(
|
||||
lang('Person.edit'),
|
||||
route_to('person-edit', $person->id),
|
||||
['variant' => 'secondary', 'iconLeft' => 'edit'],
|
||||
['class' => 'mr-2']
|
||||
) ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full max-w-sm mb-6 md:mr-4">
|
||||
<img
|
||||
src="<?= $person->image->medium_url ?>"
|
||||
alt="$person->full_name"
|
||||
class="object-cover w-full rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<section class="w-full prose">
|
||||
<?= $person->full_name ?><br />
|
||||
<a href="<?= $person->information_url ?>"><?= $person->information_url ?></a>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
|
@ -8,6 +8,10 @@ $podcastNavigation = [
|
|||
'icon' => 'mic',
|
||||
'items' => ['episode-list', 'episode-create'],
|
||||
],
|
||||
'persons' => [
|
||||
'icon' => 'folder-user',
|
||||
'items' => ['podcast-person-manage'],
|
||||
],
|
||||
'analytics' => [
|
||||
'icon' => 'line-chart',
|
||||
'items' => [
|
||||
|
|
|
@ -58,6 +58,16 @@
|
|||
$podcast->id,
|
||||
$episode->id
|
||||
) ?>"><?= lang('Episode.edit') ?></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(
|
||||
'soundbites-edit',
|
||||
$podcast->id,
|
||||
$episode->id
|
||||
) ?>"><?= lang('Episode.soundbites') ?></a>
|
||||
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
||||
'episode',
|
||||
$podcast->name,
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Person.podcast_form.title') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= lang('Person.podcast_form.title') ?> (<?= count($podcastPersons) ?>)
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('headerRight') ?>
|
||||
<?= button(
|
||||
lang('Person.create'),
|
||||
route_to('person-create'),
|
||||
['variant' => 'primary', 'iconLeft' => 'add'],
|
||||
['class' => 'mr-2']
|
||||
) ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<?= form_open(route_to('podcast-person-edit', $podcast->id), [
|
||||
'method' => 'post',
|
||||
'class' => 'flex flex-col',
|
||||
]) ?>
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<?php if ($podcastPersons): ?>
|
||||
|
||||
<?= form_section(
|
||||
lang('Person.podcast_form.manage_section_title'),
|
||||
lang('Person.podcast_form.manage_section_subtitle')
|
||||
) ?>
|
||||
|
||||
|
||||
<?= data_table(
|
||||
[
|
||||
[
|
||||
'header' => lang('Person.podcast_form.person'),
|
||||
'cell' => function ($podcastPerson) {
|
||||
return '<div class="flex">' .
|
||||
'<a href="' .
|
||||
route_to('person-view', $podcastPerson->person->id) .
|
||||
"\"><img src=\"{$podcastPerson->person->image->thumbnail_url}\" alt=\"{$podcastPerson->person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" .
|
||||
'<div class="flex flex-col ml-3">' .
|
||||
$podcastPerson->person->full_name .
|
||||
($podcastPerson->person_group && $podcastPerson->person_role
|
||||
? '<span class="text-sm text-gray-600">' .
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$podcastPerson->person_group}.label"
|
||||
) .
|
||||
' ▸ ' .
|
||||
lang(
|
||||
"PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label"
|
||||
) .
|
||||
'</span>'
|
||||
: '') .
|
||||
(empty($podcastPerson->person->information_url)
|
||||
? ''
|
||||
: "<a href=\"{$podcastPerson->person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" .
|
||||
$podcastPerson->person->information_url .
|
||||
'</a>') .
|
||||
'</div></div>';
|
||||
},
|
||||
],
|
||||
[
|
||||
'header' => lang('Common.actions'),
|
||||
'cell' => function ($podcastPerson) {
|
||||
return button(
|
||||
lang('Person.podcast_form.remove'),
|
||||
route_to(
|
||||
'podcast-person-remove',
|
||||
$podcastPerson->podcast_id,
|
||||
$podcastPerson->id
|
||||
),
|
||||
|
||||
['variant' => 'danger', 'size' => 'small']
|
||||
);
|
||||
},
|
||||
],
|
||||
],
|
||||
$podcastPersons
|
||||
) ?>
|
||||
|
||||
<?= form_section_close() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<?= form_section(
|
||||
lang('Person.podcast_form.add_section_title'),
|
||||
lang('Person.podcast_form.add_section_subtitle')
|
||||
) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.podcast_form.person'),
|
||||
'person',
|
||||
[],
|
||||
lang('Person.podcast_form.person_hint')
|
||||
) ?>
|
||||
<?= form_multiselect('person[]', $personOptions, old('person', []), [
|
||||
'id' => 'person',
|
||||
'class' => 'form-select mb-4',
|
||||
'required' => 'required',
|
||||
]) ?>
|
||||
|
||||
<?= form_label(
|
||||
lang('Person.podcast_form.group_role'),
|
||||
'group_role',
|
||||
[],
|
||||
|
||||
lang('Person.podcast_form.group_role_hint'),
|
||||
true
|
||||
) ?>
|
||||
<?= form_multiselect(
|
||||
'person_group_role[]',
|
||||
$taxonomyOptions,
|
||||
old('person_group_role', []),
|
||||
['id' => 'person_group_role', 'class' => 'form-select mb-4']
|
||||
) ?>
|
||||
|
||||
|
||||
<?= form_section_close() ?>
|
||||
<?= button(
|
||||
lang('Person.podcast_form.submit_add'),
|
||||
null,
|
||||
['variant' => 'primary'],
|
||||
['type' => 'submit', 'class' => 'self-end']
|
||||
) ?>
|
||||
<?= form_close() ?>
|
||||
|
||||
<?= $this->endSection() ?>
|
|
@ -0,0 +1,49 @@
|
|||
<?= $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-bold text-gray-500 md:text-2xl md:col-span-2 "><?= $groups[
|
||||
'group_label'
|
||||
] ?></div><?php endif; ?>
|
||||
<?php foreach ($groups['persons'] as $personId => $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-bold text-gray-700 md:text-xl"><?= $persons[
|
||||
'full_name'
|
||||
] ?></span>
|
||||
<?php if (
|
||||
!empty($persons['information_url'])
|
||||
): ?><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_slug => $role_array): ?>
|
||||
<?= $role_array['role_label'] ?>
|
||||
|
||||
<?php foreach ($role_array['is_in'] as $isIn): ?>
|
||||
<a href="<?= $isIn[
|
||||
'link'
|
||||
] ?>" class="text-sm text-gray-500 hover:underline"><?= $isIn[
|
||||
'title'
|
||||
] ?></a>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php $this->endSection(); ?>
|
|
@ -100,11 +100,28 @@
|
|||
<?= format_duration($episode->enclosure_duration) ?>
|
||||
</time>
|
||||
</div>
|
||||
<div class="flex mt-2 mb-1 space-x-2">
|
||||
<?php foreach ($persons as $person): ?>
|
||||
<?php if (!empty($person['information_url'])): ?>
|
||||
<a href="<?= $person[
|
||||
'information_url'
|
||||
] ?>" target="_blank" rel="noreferrer noopener">
|
||||
<?php endif; ?>
|
||||
<img src="<?= $person['thumbnail_url'] ?>" alt="<?= $person[
|
||||
'full_name'
|
||||
] ?>" title="[<?= $person['full_name'] ?>] <?= $person[
|
||||
'roles'
|
||||
] ?>" class="object-cover w-12 h-12 rounded-full" />
|
||||
<?php if (!empty($person['information_url'])): ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?= location_link(
|
||||
$episode->location_name,
|
||||
$episode->location_geo,
|
||||
$episode->location_osmid,
|
||||
'self-start mt-2'
|
||||
'self-start mt-2 mb-2'
|
||||
) ?>
|
||||
<audio controls preload="none" class="w-full mt-auto">
|
||||
<source src="<?= $episode->enclosure_web_url ?>" type="<?= $episode->enclosure_type ?>">
|
||||
|
|
|
@ -114,6 +114,26 @@
|
|||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-2 space-x-2">
|
||||
<?php foreach ($personArray as $person): ?>
|
||||
<?php if (!empty($person['information_url'])): ?>
|
||||
<a href="<?= $person[
|
||||
'information_url'
|
||||
] ?>" target="_blank" rel="noreferrer noopener">
|
||||
<?php endif; ?>
|
||||
<img src="<?= $person[
|
||||
'thumbnail_url'
|
||||
] ?>" alt="<?= $person[
|
||||
'full_name'
|
||||
] ?>" title="[<?= $person['full_name'] ?>] <?= $person[
|
||||
'roles'
|
||||
] ?>" class="object-cover w-12 h-12 rounded-full" />
|
||||
<?php if (!empty($person['information_url'])): ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 opacity-75">
|
||||
<?= $podcast->description_html ?>
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
"vlucas/phpdotenv": "^5.2",
|
||||
"league/html-to-markdown": "^4.10",
|
||||
"opawg/user-agents-php": "^1.0",
|
||||
"podlibre/ipcat": "^1.0"
|
||||
"podlibre/ipcat": "^1.0",
|
||||
"podlibre/podcast-namespace": "^1.0.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "1.6.*",
|
||||
|
@ -33,13 +34,19 @@
|
|||
"post-install-cmd": [
|
||||
"@php vendor/opawg/user-agents-php/src/UserAgentsGenerate.php > vendor/opawg/user-agents-php/src/UserAgents.php",
|
||||
"@php vendor/opawg/user-agents-php/src/UserAgentsRSSGenerate.php > vendor/opawg/user-agents-php/src/UserAgentsRSS.php",
|
||||
"@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php"
|
||||
"@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php",
|
||||
"@php vendor/podlibre/podcast-namespace/src/TaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-en.json > app/Language/en/PersonsTaxonomy.php",
|
||||
"@php vendor/podlibre/podcast-namespace/src/TaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-fr.json > app/Language/fr/PersonsTaxonomy.php",
|
||||
"@php vendor/podlibre/podcast-namespace/src/ReversedTaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-en.json > vendor/podlibre/podcast-namespace/src/ReversedTaxonomy.php"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@composer dump-autoload",
|
||||
"@php vendor/opawg/user-agents-php/src/UserAgentsGenerate.php > vendor/opawg/user-agents-php/src/UserAgents.php",
|
||||
"@php vendor/opawg/user-agents-php/src/UserAgentsRSSGenerate.php > vendor/opawg/user-agents-php/src/UserAgentsRSS.php",
|
||||
"@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php"
|
||||
"@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php",
|
||||
"@php vendor/podlibre/podcast-namespace/src/TaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-en.json > app/Language/en/PersonsTaxonomy.php",
|
||||
"@php vendor/podlibre/podcast-namespace/src/TaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-fr.json > app/Language/fr/PersonsTaxonomy.php",
|
||||
"@php vendor/podlibre/podcast-namespace/src/ReversedTaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-en.json > vendor/podlibre/podcast-namespace/src/ReversedTaxonomy.php"
|
||||
]
|
||||
},
|
||||
"support": {
|
||||
|
|
|
@ -1071,6 +1071,35 @@
|
|||
},
|
||||
"time": "2020-10-05T17:15:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "podlibre/podcast-namespace",
|
||||
"version": "v1.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://code.podlibre.org/podlibre/podcastnamespace",
|
||||
"reference": "4525c06ee9dd95bb745ee875d55b64a053c74cd6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Podlibre\\PodcastNamespace\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Benjamin Bellamy",
|
||||
"email": "ben@podlibre.org",
|
||||
"homepage": "https://podlibre.org/"
|
||||
}
|
||||
],
|
||||
"description": "PHP implementation for the Podcast Namespace.",
|
||||
"homepage": "https://code.podlibre.org/podlibre/podcastnamespace",
|
||||
"time": "2021-01-14T15:47:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
"version": "1.0.1",
|
||||
|
|
Loading…
Reference in New Issue