Skip to content
Snippets Groups Projects

Compare revisions

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

Source

Select target project
No results found

Target

Select target project
  • adaures/castopod
  • mkljczk/castopod-host
  • spaetz/castopod-host
  • PatrykMis/castopod
  • jonas/castopod
  • ajeremias/castopod
  • misuzu/castopod
  • KrzysztofDomanczyk/castopod
  • Behel/castopod
  • nebulon/castopod
  • ewen/castopod
  • NeoluxConsulting/castopod
  • nateritter/castopod-og
  • prcutler/castopod
14 results
Show changes
Commits on Source (2)
Showing
with 1177 additions and 51 deletions
...@@ -30,6 +30,9 @@ $RECYCLE.BIN/ ...@@ -30,6 +30,9 @@ $RECYCLE.BIN/
# Linux # Linux
*~ *~
# vim
*.swp
# KDE directory preferences # KDE directory preferences
.directory .directory
...@@ -135,6 +138,7 @@ node_modules ...@@ -135,6 +138,7 @@ node_modules
# public folder # public folder
public/* public/*
!public/media !public/media
!public/media/~person
!public/.htaccess !public/.htaccess
!public/favicon.ico !public/favicon.ico
!public/index.php !public/index.php
...@@ -144,6 +148,14 @@ public/* ...@@ -144,6 +148,14 @@ public/*
public/media/* public/media/*
!public/media/index.html !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 # Docker volumes
#------------------------- #-------------------------
......
# [1.0.0-alpha.32](https://code.podlibre.org/podlibre/castopod/compare/v1.0.0-alpha.31...v1.0.0-alpha.32) (2021-02-10)
### Features
* **person:** add podcastindex.org namespace person tag ([8acd011](https://code.podlibre.org/podlibre/castopod/commit/8acd011f13e99492ef4b44b327685bb006fe5f8f))
# [1.0.0-alpha.31](https://code.podlibre.org/podlibre/castopod/compare/v1.0.0-alpha.30...v1.0.0-alpha.31) (2020-12-23) # [1.0.0-alpha.31](https://code.podlibre.org/podlibre/castopod/compare/v1.0.0-alpha.30...v1.0.0-alpha.31) (2020-12-23)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
// //
// NOTE: this constant is updated upon release with Continuous Integration. // NOTE: this constant is updated upon release with Continuous Integration.
// //
defined('CP_VERSION') || define('CP_VERSION', '1.0.0-alpha.31'); defined('CP_VERSION') || define('CP_VERSION', '1.0.0-alpha.32');
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// App Namespace // App Namespace
......
...@@ -85,6 +85,37 @@ $routes->group( ...@@ -85,6 +85,37 @@ $routes->group(
'as' => 'my-podcasts', '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 // Podcasts
$routes->group('podcasts', function ($routes) { $routes->group('podcasts', function ($routes) {
$routes->get('/', 'Podcast::list', [ $routes->get('/', 'Podcast::list', [
...@@ -124,6 +155,25 @@ $routes->group( ...@@ -124,6 +155,25 @@ $routes->group(
'filter' => 'permission:podcasts-delete', '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->group('analytics', function ($routes) {
$routes->get('/', 'Podcast::viewAnalytics/$1', [ $routes->get('/', 'Podcast::viewAnalytics/$1', [
'as' => 'podcast-analytics', 'as' => 'podcast-analytics',
...@@ -276,6 +326,30 @@ $routes->group( ...@@ -276,6 +326,30 @@ $routes->group(
'filter' => 'permission:podcast_episodes-edit', '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) { ...@@ -497,6 +571,7 @@ $routes->group('@(:podcastName)', function ($routes) {
$routes->head('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']); $routes->head('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
$routes->get('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']); $routes->get('/(:slug)', 'Page/$1', ['as' => 'page']);
/** /**
......
<?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();
}
}
<?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; ...@@ -13,6 +13,9 @@ use App\Models\LanguageModel;
use App\Models\PodcastModel; use App\Models\PodcastModel;
use App\Models\EpisodeModel; use App\Models\EpisodeModel;
use App\Models\PlatformModel; use App\Models\PlatformModel;
use App\Models\PersonModel;
use App\Models\PodcastPersonModel;
use App\Models\EpisodePersonModel;
use Config\Services; use Config\Services;
use League\HTMLToMarkdown\HtmlConverter; use League\HTMLToMarkdown\HtmlConverter;
...@@ -150,7 +153,7 @@ class PodcastImport extends BaseController ...@@ -150,7 +153,7 @@ class PodcastImport extends BaseController
: $nsItunes->complete === 'yes', : $nsItunes->complete === 'yes',
'location_name' => !$nsPodcast->location 'location_name' => !$nsPodcast->location
? null ? null
: $nsPodcast->location->attributes()['name'], : $nsPodcast->location,
'location_geo' => 'location_geo' =>
!$nsPodcast->location || !$nsPodcast->location ||
empty($nsPodcast->location->attributes()['geo']) empty($nsPodcast->location->attributes()['geo'])
...@@ -158,9 +161,9 @@ class PodcastImport extends BaseController ...@@ -158,9 +161,9 @@ class PodcastImport extends BaseController
: $nsPodcast->location->attributes()['geo'], : $nsPodcast->location->attributes()['geo'],
'location_osmid' => 'location_osmid' =>
!$nsPodcast->location || !$nsPodcast->location ||
empty($nsPodcast->location->attributes()['osmid']) empty($nsPodcast->location->attributes()['osm'])
? null ? null
: $nsPodcast->location->attributes()['osmid'], : $nsPodcast->location->attributes()['osm'],
'created_by' => user(), 'created_by' => user(),
'updated_by' => user(), 'updated_by' => user(),
]); ]);
...@@ -200,40 +203,40 @@ class PodcastImport extends BaseController ...@@ -200,40 +203,40 @@ class PodcastImport extends BaseController
$podcastAdminGroup->id $podcastAdminGroup->id
); );
$platformModel = new PlatformModel();
$podcastsPlatformsData = []; $podcastsPlatformsData = [];
foreach ($nsPodcast->id as $podcastingPlatform) { $platformTypes = [
$slug = $podcastingPlatform->attributes()['platform']; ['name' => 'podcasting', 'elements' => $nsPodcast->id],
$platformModel->getOrCreatePlatform($slug, 'podcasting'); ['name' => 'social', 'elements' => $nsPodcast->social],
array_push($podcastsPlatformsData, [ ['name' => 'funding', 'elements' => $nsPodcast->funding],
'platform_slug' => $slug, ];
'podcast_id' => $newPodcastId, $platformModel = new PlatformModel();
'link_url' => $podcastingPlatform->attributes()['url'], foreach ($platformTypes as $platformType) {
'link_content' => $podcastingPlatform->attributes()['id'], foreach ($platformType['elements'] as $platform) {
'is_visible' => false, $platformLabel = $platform->attributes()['platform'];
]); $platformSlug = slugify($platformLabel);
} if (!$platformModel->getPlatform($platformSlug)) {
foreach ($nsPodcast->social as $socialPlatform) { if (
$slug = $socialPlatform->attributes()['platform']; !$platformModel->createPlatform(
$platformModel->getOrCreatePlatform($slug, 'social'); $platformSlug,
array_push($podcastsPlatformsData, [ $platformType['name'],
'platform_slug' => $socialPlatform->attributes()['platform'], $platformLabel,
'podcast_id' => $newPodcastId, ''
'link_url' => $socialPlatform->attributes()['url'], )
'link_content' => $socialPlatform, ) {
'is_visible' => false, return redirect()
]); ->back()
} ->withInput()
foreach ($nsPodcast->funding as $fundingPlatform) { ->with('errors', $platformModel->errors());
$slug = $fundingPlatform->attributes()['platform']; }
$platformModel->getOrCreatePlatform($slug, 'funding'); }
array_push($podcastsPlatformsData, [ array_push($podcastsPlatformsData, [
'platform_slug' => $fundingPlatform->attributes()['platform'], 'platform_slug' => $platformSlug,
'podcast_id' => $newPodcastId, 'podcast_id' => $newPodcastId,
'link_url' => $fundingPlatform->attributes()['url'], 'link_url' => $platform->attributes()['url'],
'link_content' => $fundingPlatform->attributes()['id'], 'link_content' => $platform->attributes()['id'],
'is_visible' => false, 'is_visible' => false,
]); ]);
}
} }
if (count($podcastsPlatformsData) > 1) { if (count($podcastsPlatformsData) > 1) {
$platformModel->createPodcastPlatforms( $platformModel->createPodcastPlatforms(
...@@ -242,6 +245,54 @@ class PodcastImport extends BaseController ...@@ -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(); $numberItems = $feed->channel[0]->item->count();
$lastItem = $lastItem =
!empty($this->request->getPost('max_episodes')) && !empty($this->request->getPost('max_episodes')) &&
...@@ -251,6 +302,7 @@ class PodcastImport extends BaseController ...@@ -251,6 +302,7 @@ class PodcastImport extends BaseController
$slugs = []; $slugs = [];
//////////////////////////////////////////////////////////////////
// For each Episode: // For each Episode:
for ($itemNumber = 1; $itemNumber <= $lastItem; $itemNumber++) { for ($itemNumber = 1; $itemNumber <= $lastItem; $itemNumber++) {
$item = $feed->channel[0]->item[$numberItems - $itemNumber]; $item = $feed->channel[0]->item[$numberItems - $itemNumber];
...@@ -326,7 +378,7 @@ class PodcastImport extends BaseController ...@@ -326,7 +378,7 @@ class PodcastImport extends BaseController
: $nsItunes->block === 'yes', : $nsItunes->block === 'yes',
'location_name' => !$nsPodcast->location 'location_name' => !$nsPodcast->location
? null ? null
: $nsPodcast->location->attributes()['name'], : $nsPodcast->location,
'location_geo' => 'location_geo' =>
!$nsPodcast->location || !$nsPodcast->location ||
empty($nsPodcast->location->attributes()['geo']) empty($nsPodcast->location->attributes()['geo'])
...@@ -334,9 +386,9 @@ class PodcastImport extends BaseController ...@@ -334,9 +386,9 @@ class PodcastImport extends BaseController
: $nsPodcast->location->attributes()['geo'], : $nsPodcast->location->attributes()['geo'],
'location_osmid' => 'location_osmid' =>
!$nsPodcast->location || !$nsPodcast->location ||
empty($nsPodcast->location->attributes()['osmid']) empty($nsPodcast->location->attributes()['osm'])
? null ? null
: $nsPodcast->location->attributes()['osmid'], : $nsPodcast->location->attributes()['osm'],
'created_by' => user(), 'created_by' => user(),
'updated_by' => user(), 'updated_by' => user(),
'published_at' => strtotime($item->pubDate), 'published_at' => strtotime($item->pubDate),
...@@ -344,13 +396,62 @@ class PodcastImport extends BaseController ...@@ -344,13 +396,62 @@ class PodcastImport extends BaseController
$episodeModel = new EpisodeModel(); $episodeModel = new EpisodeModel();
if (!$episodeModel->insert($newEpisode)) { if (!($newEpisodeId = $episodeModel->insert($newEpisode, true))) {
// FIXME: What shall we do? // FIXME: What shall we do?
return redirect() return redirect()
->back() ->back()
->withInput() ->withInput()
->with('errors', $episodeModel->errors()); ->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(); $db->transComplete();
......
<?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 ...@@ -54,11 +54,55 @@ class Episode extends BaseController
$this->podcast->type $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 = [ $data = [
'previousEpisode' => $previousNextEpisodes['previous'], 'previousEpisode' => $previousNextEpisodes['previous'],
'nextEpisode' => $previousNextEpisodes['next'], 'nextEpisode' => $previousNextEpisodes['next'],
'podcast' => $this->podcast, 'podcast' => $this->podcast,
'episode' => $this->episode, 'episode' => $this->episode,
'persons' => $persons,
]; ];
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode( $secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
namespace App\Controllers; namespace App\Controllers;
use App\Models\PageModel; use App\Models\PageModel;
use App\Models\CreditModel;
use App\Models\PodcastModel;
class Page extends BaseController class Page extends BaseController
{ {
...@@ -42,4 +44,137 @@ class Page extends BaseController ...@@ -42,4 +44,137 @@ class Page extends BaseController
]; ];
return view('page', $data); 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 ...@@ -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 = [ $data = [
'podcast' => $this->podcast, 'podcast' => $this->podcast,
'episodesNav' => $episodesNavigation, 'episodesNav' => $episodesNavigation,
...@@ -119,6 +162,7 @@ class Podcast extends BaseController ...@@ -119,6 +162,7 @@ class Podcast extends BaseController
$yearQuery, $yearQuery,
$seasonQuery $seasonQuery
), ),
'personArray' => $persons,
]; ];
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode( $secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
......
...@@ -41,11 +41,9 @@ class AddPlatforms extends Migration ...@@ -41,11 +41,9 @@ class AddPlatforms extends Migration
'default' => null, 'default' => null,
], ],
]); ]);
$this->forge->addField('`created_at` timestamp NOT NULL DEFAULT NOW()');
$this->forge->addField( $this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()' '`updated_at` timestamp NOT NULL DEFAULT NOW() ON UPDATE NOW()'
);
$this->forge->addField(
'`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()'
); );
$this->forge->addKey('slug', true); $this->forge->addKey('slug', true);
$this->forge->createTable('platforms'); $this->forge->createTable('platforms');
......
...@@ -40,12 +40,6 @@ class AddPodcastsPlatforms extends Migration ...@@ -40,12 +40,6 @@ class AddPodcastsPlatforms extends Migration
'constraint' => 1, 'constraint' => 1,
'default' => 0, 'default' => 0,
], ],
'created_at' => [
'type' => 'DATETIME',
],
'updated_at' => [
'type' => 'DATETIME',
],
]); ]);
$this->forge->addPrimaryKey(['podcast_id', 'platform_slug']); $this->forge->addPrimaryKey(['podcast_id', 'platform_slug']);
......
<?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');
}
}
<?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');
}
}
<?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');
}
}
<?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 ...@@ -198,6 +198,33 @@ class AuthSeeder extends Seeder
'has_permission' => ['podcast_admin'], '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) static function getGroupIdByName($name, $dataGroups)
......
...@@ -47,6 +47,13 @@ class PlatformSeeder extends Seeder ...@@ -47,6 +47,13 @@ class PlatformSeeder extends Seeder
'home_url' => 'https://www.blubrry.com/', 'home_url' => 'https://www.blubrry.com/',
'submit_url' => 'https://www.blubrry.com/addpodcast.php', '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', 'slug' => 'castbox',
'type' => 'podcasting', 'type' => 'podcasting',
......
<?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"
);
}
}
}