feat: add pages table to store custom instance pages (eg. legal-notice, cookie policy, etc.)
- add pages migration, model and entity - add page controllers - update routes config to input page forms and page view in public - fix markdow editor focus area - show pages links in public side footer closes #24
This commit is contained in:
parent
a1a28de702
commit
9c224a8ac6
|
@ -30,7 +30,7 @@ $routes->setAutoRoute(false);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$routes->addPlaceholder('podcastName', '[a-zA-Z0-9\_]{1,191}');
|
$routes->addPlaceholder('podcastName', '[a-zA-Z0-9\_]{1,191}');
|
||||||
$routes->addPlaceholder('episodeSlug', '[a-zA-Z0-9\-]{1,191}');
|
$routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,191}');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
|
@ -53,15 +53,6 @@ $routes->group(config('App')->installGateway, function ($routes) {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Public routes
|
|
||||||
$routes->group('@(:podcastName)', function ($routes) {
|
|
||||||
$routes->get('/', 'Podcast/$1', ['as' => 'podcast']);
|
|
||||||
$routes->get('(:episodeSlug)', 'Episode/$1/$2', [
|
|
||||||
'as' => 'episode',
|
|
||||||
]);
|
|
||||||
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Route for podcast audio file analytics (/stats/podcast_id/episode_id/podcast_folder/filename.mp3)
|
// Route for podcast audio file analytics (/stats/podcast_id/episode_id/podcast_folder/filename.mp3)
|
||||||
$routes->add('stats/(:num)/(:num)/(:any)', 'Analytics::hit/$1/$2/$3', [
|
$routes->add('stats/(:num)/(:num)/(:any)', 'Analytics::hit/$1/$2/$3', [
|
||||||
'as' => 'analytics_hit',
|
'as' => 'analytics_hit',
|
||||||
|
@ -80,10 +71,6 @@ $routes->group(
|
||||||
'as' => 'admin',
|
'as' => 'admin',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$routes->get('my-podcasts', 'Podcast::myPodcasts', [
|
|
||||||
'as' => 'my-podcasts',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Podcasts
|
// Podcasts
|
||||||
$routes->group('podcasts', function ($routes) {
|
$routes->group('podcasts', function ($routes) {
|
||||||
$routes->get('/', 'Podcast::list', [
|
$routes->get('/', 'Podcast::list', [
|
||||||
|
@ -201,6 +188,27 @@ $routes->group(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pages
|
||||||
|
$routes->group('pages', function ($routes) {
|
||||||
|
$routes->get('/', 'Page::list', ['as' => 'page-list']);
|
||||||
|
$routes->get('new', 'Page::create', [
|
||||||
|
'as' => 'page-create',
|
||||||
|
]);
|
||||||
|
$routes->post('new', 'Page::attemptCreate');
|
||||||
|
|
||||||
|
$routes->group('(:num)', function ($routes) {
|
||||||
|
$routes->get('/', 'Page::view/$1', ['as' => 'page-view']);
|
||||||
|
$routes->get('edit', 'Page::edit/$1', [
|
||||||
|
'as' => 'page-edit',
|
||||||
|
]);
|
||||||
|
$routes->post('edit', 'Page::attemptEdit/$1');
|
||||||
|
|
||||||
|
$routes->add('delete', 'Page::delete/$1', [
|
||||||
|
'as' => 'page-delete',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
$routes->group('users', function ($routes) {
|
$routes->group('users', function ($routes) {
|
||||||
$routes->get('/', 'User::list', [
|
$routes->get('/', 'User::list', [
|
||||||
|
@ -294,6 +302,16 @@ $routes->group(config('App')->authGateway, function ($routes) {
|
||||||
$routes->post('reset-password', 'Auth::attemptReset');
|
$routes->post('reset-password', 'Auth::attemptReset');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Public routes
|
||||||
|
$routes->group('@(:podcastName)', function ($routes) {
|
||||||
|
$routes->get('/', 'Podcast/$1', ['as' => 'podcast']);
|
||||||
|
$routes->get('(:slug)', 'Episode/$1/$2', [
|
||||||
|
'as' => 'episode',
|
||||||
|
]);
|
||||||
|
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
||||||
|
});
|
||||||
|
$routes->get('/(:slug)', 'Page/$1', ['as' => 'page']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* Additional Routing
|
* Additional Routing
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Validation
|
||||||
\CodeIgniter\Validation\FormatRules::class,
|
\CodeIgniter\Validation\FormatRules::class,
|
||||||
\CodeIgniter\Validation\FileRules::class,
|
\CodeIgniter\Validation\FileRules::class,
|
||||||
\CodeIgniter\Validation\CreditCardRules::class,
|
\CodeIgniter\Validation\CreditCardRules::class,
|
||||||
|
\App\Validation\Rules::class,
|
||||||
\Myth\Auth\Authentication\Passwords\ValidationRules::class,
|
\Myth\Auth\Authentication\Passwords\ValidationRules::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -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\PageModel;
|
||||||
|
|
||||||
|
class Page extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \App\Entities\Page|null
|
||||||
|
*/
|
||||||
|
protected $page;
|
||||||
|
|
||||||
|
public function _remap($method, ...$params)
|
||||||
|
{
|
||||||
|
if (count($params) > 0) {
|
||||||
|
if (!($this->page = (new PageModel())->find($params[0]))) {
|
||||||
|
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->$method();
|
||||||
|
}
|
||||||
|
|
||||||
|
function list()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'pages' => (new PageModel())->findAll(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('admin/page/list', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function view()
|
||||||
|
{
|
||||||
|
return view('admin/page/view', ['page' => $this->page]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create()
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
return view('admin/page/create');
|
||||||
|
}
|
||||||
|
|
||||||
|
function attemptCreate()
|
||||||
|
{
|
||||||
|
$page = new \App\Entities\Page([
|
||||||
|
'title' => $this->request->getPost('title'),
|
||||||
|
'slug' => $this->request->getPost('slug'),
|
||||||
|
'content' => $this->request->getPost('content'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pageModel = new PageModel();
|
||||||
|
|
||||||
|
if (!$pageModel->save($page)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $pageModel->errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('page-list')
|
||||||
|
->with(
|
||||||
|
'message',
|
||||||
|
lang('Page.messages.createSuccess', [
|
||||||
|
'pageTitle' => $page->title,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit()
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
replace_breadcrumb_params([0 => $this->page->title]);
|
||||||
|
return view('admin/page/edit', ['page' => $this->page]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function attemptEdit()
|
||||||
|
{
|
||||||
|
$this->page->title = $this->request->getPost('title');
|
||||||
|
$this->page->slug = $this->request->getPost('slug');
|
||||||
|
$this->page->content = $this->request->getPost('content');
|
||||||
|
|
||||||
|
$pageModel = new PageModel();
|
||||||
|
|
||||||
|
if (!$pageModel->save($this->page)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $pageModel->errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('page-list');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
(new PageModel())->delete($this->page->id);
|
||||||
|
|
||||||
|
return redirect()->route('page-list');
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,22 +31,15 @@ class Podcast extends BaseController
|
||||||
return $this->$method();
|
return $this->$method();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function myPodcasts()
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'podcasts' => (new PodcastModel())->getUserPodcasts(user()->id),
|
|
||||||
];
|
|
||||||
|
|
||||||
return view('admin/podcast/list', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function list()
|
public function list()
|
||||||
{
|
{
|
||||||
if (!has_permission('podcasts-list')) {
|
if (!has_permission('podcasts-list')) {
|
||||||
return redirect()->route('my-podcasts');
|
$data = [
|
||||||
}
|
'podcasts' => (new PodcastModel())->getUserPodcasts(user()->id),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
$data = ['podcasts' => (new PodcastModel())->findAll()];
|
$data = ['podcasts' => (new PodcastModel())->findAll()];
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin/podcast/list', $data);
|
return view('admin/podcast/list', $data);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +148,7 @@ class Podcast extends BaseController
|
||||||
|
|
||||||
$db->transComplete();
|
$db->transComplete();
|
||||||
|
|
||||||
return redirect()->route('podcast-list');
|
return redirect()->route('podcast-view', [$newPodcastId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit()
|
public function edit()
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Models\PageModel;
|
||||||
|
|
||||||
|
class Page extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \App\Entities\Page|null
|
||||||
|
*/
|
||||||
|
protected $page;
|
||||||
|
|
||||||
|
public function _remap($method, ...$params)
|
||||||
|
{
|
||||||
|
if (count($params) > 0) {
|
||||||
|
if (
|
||||||
|
!($this->page = (new PageModel())
|
||||||
|
->where('slug', $params[0])
|
||||||
|
->first())
|
||||||
|
) {
|
||||||
|
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->$method();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
// The page cache is set to a decade so it is deleted manually upon page update
|
||||||
|
$this->cachePage(DECADE);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'page' => $this->page,
|
||||||
|
];
|
||||||
|
return view('page', $data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,19 +54,17 @@ class AddPodcasts extends Migration
|
||||||
'constraint' => 1,
|
'constraint' => 1,
|
||||||
'default' => 0,
|
'default' => 0,
|
||||||
],
|
],
|
||||||
'author' => [
|
|
||||||
'type' => 'VARCHAR',
|
|
||||||
'constraint' => 1024,
|
|
||||||
'null' => true,
|
|
||||||
],
|
|
||||||
'owner_name' => [
|
'owner_name' => [
|
||||||
'type' => 'VARCHAR',
|
'type' => 'VARCHAR',
|
||||||
'constraint' => 1024,
|
'constraint' => 1024,
|
||||||
'null' => true,
|
|
||||||
],
|
],
|
||||||
'owner_email' => [
|
'owner_email' => [
|
||||||
'type' => 'VARCHAR',
|
'type' => 'VARCHAR',
|
||||||
'constraint' => 1024,
|
'constraint' => 1024,
|
||||||
|
],
|
||||||
|
'author' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 1024,
|
||||||
'null' => true,
|
'null' => true,
|
||||||
],
|
],
|
||||||
'type' => [
|
'type' => [
|
||||||
|
|
|
@ -41,7 +41,6 @@ class AddEpisodes extends Migration
|
||||||
'type' => 'VARCHAR',
|
'type' => 'VARCHAR',
|
||||||
'constraint' => 1024,
|
'constraint' => 1024,
|
||||||
],
|
],
|
||||||
|
|
||||||
'description' => [
|
'description' => [
|
||||||
'type' => 'TEXT',
|
'type' => 'TEXT',
|
||||||
'null' => true,
|
'null' => true,
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AddLanguages
|
||||||
|
* Creates languages 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 AddPages extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->forge->addField([
|
||||||
|
'id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'unsigned' => true,
|
||||||
|
'auto_increment' => true,
|
||||||
|
],
|
||||||
|
'title' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 1024,
|
||||||
|
],
|
||||||
|
'slug' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 191,
|
||||||
|
'unique' => true,
|
||||||
|
],
|
||||||
|
'content' => [
|
||||||
|
'type' => 'TEXT',
|
||||||
|
],
|
||||||
|
'created_at' => [
|
||||||
|
'type' => 'TIMESTAMP',
|
||||||
|
],
|
||||||
|
'updated_at' => [
|
||||||
|
'type' => 'TIMESTAMP',
|
||||||
|
],
|
||||||
|
'deleted_at' => [
|
||||||
|
'type' => 'DATETIME',
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$this->forge->addKey('id', true);
|
||||||
|
$this->forge->createTable('pages');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->forge->dropTable('pages');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?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 League\CommonMark\CommonMarkConverter;
|
||||||
|
|
||||||
|
class Page extends Entity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $link;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $content_html;
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'id' => 'integer',
|
||||||
|
'title' => 'string',
|
||||||
|
'slug' => 'string',
|
||||||
|
'content' => 'string',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function getLink()
|
||||||
|
{
|
||||||
|
return base_url($this->attributes['slug']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContentHtml()
|
||||||
|
{
|
||||||
|
$converter = new CommonMarkConverter([
|
||||||
|
'html_input' => 'strip',
|
||||||
|
'allow_unsafe_links' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $converter->convertToHtml($this->attributes['content']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Models\PageModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns instance pages as links inside nav tag
|
||||||
|
*
|
||||||
|
* @return string html pages navigation
|
||||||
|
*/
|
||||||
|
function render_page_links()
|
||||||
|
{
|
||||||
|
$pages = (new PageModel())->findAll();
|
||||||
|
$links = '';
|
||||||
|
foreach ($pages as $page) {
|
||||||
|
$links .= anchor($page->link, $page->title, [
|
||||||
|
'class' => 'px-2 underline hover:no-underline',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<nav class="inline-flex">' . $links . '</nav>';
|
||||||
|
}
|
|
@ -95,13 +95,9 @@ function get_rss_feed($podcast)
|
||||||
$channel->addChild('author', $podcast->author, $itunes_namespace);
|
$channel->addChild('author', $podcast->author, $itunes_namespace);
|
||||||
$channel->addChild('link', $podcast->link);
|
$channel->addChild('link', $podcast->link);
|
||||||
|
|
||||||
if ($podcast->owner_name || $podcast->owner_email) {
|
|
||||||
$owner = $channel->addChild('owner', null, $itunes_namespace);
|
$owner = $channel->addChild('owner', null, $itunes_namespace);
|
||||||
$podcast->owner_name &&
|
|
||||||
$owner->addChild('name', $podcast->owner_name, $itunes_namespace);
|
$owner->addChild('name', $podcast->owner_name, $itunes_namespace);
|
||||||
$podcast->owner_email &&
|
|
||||||
$owner->addChild('email', $podcast->owner_email, $itunes_namespace);
|
$owner->addChild('email', $podcast->owner_email, $itunes_namespace);
|
||||||
}
|
|
||||||
|
|
||||||
$channel->addChild('type', $podcast->type, $itunes_namespace);
|
$channel->addChild('type', $podcast->type, $itunes_namespace);
|
||||||
$podcast->copyright && $channel->addChild('copyright', $podcast->copyright);
|
$podcast->copyright && $channel->addChild('copyright', $podcast->copyright);
|
||||||
|
|
|
@ -10,11 +10,13 @@ return [
|
||||||
'dashboard' => 'Dashboard',
|
'dashboard' => 'Dashboard',
|
||||||
'podcasts' => 'Podcasts',
|
'podcasts' => 'Podcasts',
|
||||||
'users' => 'Users',
|
'users' => 'Users',
|
||||||
|
'pages' => 'Pages',
|
||||||
'admin' => 'Home',
|
'admin' => 'Home',
|
||||||
'my-podcasts' => 'My podcasts',
|
|
||||||
'podcast-list' => 'All podcasts',
|
'podcast-list' => 'All podcasts',
|
||||||
'podcast-create' => 'New podcast',
|
'podcast-create' => 'New podcast',
|
||||||
'user-list' => 'All users',
|
'user-list' => 'All users',
|
||||||
'user-create' => 'New user',
|
'user-create' => 'New user',
|
||||||
|
'page-list' => 'All pages',
|
||||||
|
'page-create' => 'New Page',
|
||||||
'go_to_website' => 'Go to website',
|
'go_to_website' => 'Go to website',
|
||||||
];
|
];
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
return [
|
return [
|
||||||
'label' => 'breadcrumb',
|
'label' => 'breadcrumb',
|
||||||
config('App')->adminGateway => 'Home',
|
config('App')->adminGateway => 'Home',
|
||||||
'my-podcasts' => 'my podcasts',
|
|
||||||
'podcasts' => 'podcasts',
|
'podcasts' => 'podcasts',
|
||||||
'episodes' => 'episodes',
|
'episodes' => 'episodes',
|
||||||
'contributors' => 'contributors',
|
'contributors' => 'contributors',
|
||||||
|
'pages' => 'pages',
|
||||||
'add' => 'add',
|
'add' => 'add',
|
||||||
'new' => 'new',
|
'new' => 'new',
|
||||||
'edit' => 'edit',
|
'edit' => 'edit',
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
'all_pages' => 'All pages',
|
||||||
|
'create' => 'New page',
|
||||||
|
'go_to_page' => 'Go to page',
|
||||||
|
'edit' => 'Edit page',
|
||||||
|
'delete' => 'Delete page',
|
||||||
|
'form' => [
|
||||||
|
'title' => 'Title',
|
||||||
|
'slug' => 'Slug',
|
||||||
|
'content' => 'Content',
|
||||||
|
'submit_create' => 'Create page',
|
||||||
|
'submit_edit' => 'Save',
|
||||||
|
],
|
||||||
|
'messages' => [
|
||||||
|
'createSuccess' => 'The {pageTitle} page was created successfully!',
|
||||||
|
],
|
||||||
|
];
|
|
@ -42,15 +42,15 @@ return [
|
||||||
'explicit' => 'Explicit',
|
'explicit' => 'Explicit',
|
||||||
'explicit_help' =>
|
'explicit_help' =>
|
||||||
'The podcast parental advisory information. Does it contain explicit content?',
|
'The podcast parental advisory information. Does it contain explicit content?',
|
||||||
'author' => 'Author',
|
|
||||||
'author_help' =>
|
|
||||||
'The group responsible for creating the show. Show author most often refers to the parent company or network of a podcast. This field is sometimes labeled as ’Author’.',
|
|
||||||
'owner_name' => 'Owner name',
|
'owner_name' => 'Owner name',
|
||||||
'owner_name_help' =>
|
'owner_name_help' =>
|
||||||
'The podcast owner contact name. For administrative use only. It will not be shown on podcasts platforms (such as Apple Podcasts) nor players (such as Podcast Addict) but it is visible in the public RSS feed.',
|
'The podcast owner contact name. For administrative use only. It will not be shown on podcasts platforms (such as Apple Podcasts) nor players (such as Podcast Addict) but it is visible in the public RSS feed.',
|
||||||
'owner_email' => 'Owner email',
|
'owner_email' => 'Owner email',
|
||||||
'owner_email_help' =>
|
'owner_email_help' =>
|
||||||
'The podcast owner contact e-mail. For administrative use only. It will mostly be used by some platforms to verify this podcast ownerhip. It will not be shown on podcasts platforms (such as Apple Podcasts) nor players (such as Podcast Addict) but it is visible in the public RSS feed.',
|
'The podcast owner contact e-mail. For administrative use only. It will mostly be used by some platforms to verify this podcast ownerhip. It will not be shown on podcasts platforms (such as Apple Podcasts) nor players (such as Podcast Addict) but it is visible in the public RSS feed.',
|
||||||
|
'author' => 'Author',
|
||||||
|
'author_help' =>
|
||||||
|
'The group responsible for creating the show. Show author most often refers to the parent company or network of a podcast. This field is sometimes labeled as ’Author’.',
|
||||||
'type' => [
|
'type' => [
|
||||||
'label' => 'Type',
|
'label' => 'Type',
|
||||||
'episodic' => 'Episodic',
|
'episodic' => 'Episodic',
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
'not_in_protected_slugs' =>
|
||||||
|
'The {field} field conflicts with one of the gateway routes (admin, auth or install).',
|
||||||
|
];
|
|
@ -53,8 +53,10 @@ class EpisodeModel extends Model
|
||||||
];
|
];
|
||||||
protected $validationMessages = [];
|
protected $validationMessages = [];
|
||||||
|
|
||||||
protected $afterInsert = ['writeEnclosureMetadata', 'clearCache'];
|
protected $afterInsert = ['writeEnclosureMetadata'];
|
||||||
protected $afterUpdate = ['writeEnclosureMetadata', 'clearCache'];
|
// clear cache beforeUpdate because if slug changes, so will the episode link
|
||||||
|
protected $beforeUpdate = ['clearCache'];
|
||||||
|
protected $afterUpdate = ['writeEnclosureMetadata'];
|
||||||
protected $beforeDelete = ['clearCache'];
|
protected $beforeDelete = ['clearCache'];
|
||||||
|
|
||||||
protected function writeEnclosureMetadata(array $data)
|
protected function writeEnclosureMetadata(array $data)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?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 PageModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'pages';
|
||||||
|
protected $primaryKey = 'id';
|
||||||
|
|
||||||
|
protected $allowedFields = ['id', 'title', 'slug', 'content'];
|
||||||
|
|
||||||
|
protected $returnType = \App\Entities\Page::class;
|
||||||
|
protected $useSoftDeletes = true;
|
||||||
|
|
||||||
|
protected $useTimestamps = true;
|
||||||
|
|
||||||
|
protected $validationRules = [
|
||||||
|
'title' => 'required',
|
||||||
|
'slug' =>
|
||||||
|
'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]|not_in_protected_slugs',
|
||||||
|
'content' => 'required',
|
||||||
|
];
|
||||||
|
protected $validationMessages = [];
|
||||||
|
|
||||||
|
// Before update because slug might change
|
||||||
|
protected $beforeUpdate = ['clearCache'];
|
||||||
|
protected $beforeDelete = ['clearCache'];
|
||||||
|
|
||||||
|
protected function clearCache(array $data)
|
||||||
|
{
|
||||||
|
$page = (new PageModel())->find(
|
||||||
|
is_array($data['id']) ? $data['id'][0] : $data['id']
|
||||||
|
);
|
||||||
|
|
||||||
|
// delete page cache
|
||||||
|
cache()->delete(md5($page->link));
|
||||||
|
|
||||||
|
// Clear the cache of all podcast and episode pages
|
||||||
|
// TODO: change the logic of page caching to prevent clearing all cache every time
|
||||||
|
cache()->clean();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,9 +25,9 @@ class PodcastModel extends Model
|
||||||
'language',
|
'language',
|
||||||
'category',
|
'category',
|
||||||
'explicit',
|
'explicit',
|
||||||
'author',
|
|
||||||
'owner_name',
|
'owner_name',
|
||||||
'owner_email',
|
'owner_email',
|
||||||
|
'author',
|
||||||
'type',
|
'type',
|
||||||
'copyright',
|
'copyright',
|
||||||
'block',
|
'block',
|
||||||
|
@ -50,6 +50,7 @@ class PodcastModel extends Model
|
||||||
'image_uri' => 'required',
|
'image_uri' => 'required',
|
||||||
'language' => 'required',
|
'language' => 'required',
|
||||||
'category' => 'required',
|
'category' => 'required',
|
||||||
|
'owner_name' => 'required',
|
||||||
'owner_email' => 'required|valid_email',
|
'owner_email' => 'required|valid_email',
|
||||||
'type' => 'required',
|
'type' => 'required',
|
||||||
'created_by' => 'required',
|
'created_by' => 'required',
|
||||||
|
@ -57,8 +58,8 @@ class PodcastModel extends Model
|
||||||
];
|
];
|
||||||
protected $validationMessages = [];
|
protected $validationMessages = [];
|
||||||
|
|
||||||
protected $afterInsert = ['clearCache'];
|
// clear cache before update if by any chance, the podcast name changes, and so will the podcast link
|
||||||
protected $afterUpdate = ['clearCache'];
|
protected $beforeUpdate = ['clearCache'];
|
||||||
protected $beforeDelete = ['clearCache'];
|
protected $beforeDelete = ['clearCache'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Validation;
|
||||||
|
|
||||||
|
class Rules
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Value should not be within the array of protected slugs (adminGateway, authGateway or installGateway)
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function not_in_protected_slugs(string $value = null): bool
|
||||||
|
{
|
||||||
|
$appConfig = config('App');
|
||||||
|
$protectedSlugs = [
|
||||||
|
$appConfig->adminGateway,
|
||||||
|
$appConfig->authGateway,
|
||||||
|
$appConfig->installGateway,
|
||||||
|
];
|
||||||
|
return !in_array($value, $protectedSlugs, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z"/>
|
||||||
|
<path d="M5 8v12h14V8H5zm0-2h14V4H5v2zm15 16H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1zM7 10h4v4H7v-4zm0 6h10v2H7v-2zm6-5h4v2h-4v-2z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 299 B |
|
@ -38,13 +38,7 @@ class ProseMirrorView {
|
||||||
|
|
||||||
constructor(target: HTMLTextAreaElement, content: string) {
|
constructor(target: HTMLTextAreaElement, content: string) {
|
||||||
this.editorContainer = document.createElement("div");
|
this.editorContainer = document.createElement("div");
|
||||||
this.editorContainer.classList.add(
|
this.editorContainer.classList.add("bg-white", "border");
|
||||||
"bg-white",
|
|
||||||
"border",
|
|
||||||
"px-2",
|
|
||||||
"min-h-full",
|
|
||||||
"prose-sm"
|
|
||||||
);
|
|
||||||
this.editorContainer.style.minHeight = "200px";
|
this.editorContainer.style.minHeight = "200px";
|
||||||
const editor = target.parentNode?.insertBefore(
|
const editor = target.parentNode?.insertBefore(
|
||||||
this.editorContainer,
|
this.editorContainer,
|
||||||
|
@ -64,6 +58,10 @@ class ProseMirrorView {
|
||||||
target.innerHTML = this.content;
|
target.innerHTML = this.content;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
attributes: {
|
||||||
|
class: "prose-sm px-3 py-2 overflow-y-auto",
|
||||||
|
style: "min-height: 200px; max-height: 500px",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
<?= helper('page') ?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<title>Castopod</title>
|
<title><?= $this->renderSection('title') ?></title>
|
||||||
<meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
|
<meta name="description" content="Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience."/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
||||||
|
@ -22,7 +23,8 @@
|
||||||
<main class="container flex-1 px-4 py-10 mx-auto">
|
<main class="container flex-1 px-4 py-10 mx-auto">
|
||||||
<?= $this->renderSection('content') ?>
|
<?= $this->renderSection('content') ?>
|
||||||
</main>
|
</main>
|
||||||
<footer class="container px-2 py-4 mx-auto text-sm text-right border-t">
|
<footer class="container flex justify-between px-2 py-4 mx-auto text-sm text-right border-t">
|
||||||
Powered by <a class="underline hover:no-underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod</a>, a <a class="underline hover:no-underline" href="https://podlibre.org/" target="_blank" rel="noreferrer noopener">Podlibre</a> initiative.
|
<?= render_page_links() ?>
|
||||||
|
<p>Powered by <a class="underline hover:no-underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod</a>, a <a class="underline hover:no-underline" href="https://podlibre.org/" target="_blank" rel="noreferrer noopener">Podlibre</a> initiative.</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -3,9 +3,10 @@ $navigation = [
|
||||||
'dashboard' => ['icon' => 'dashboard', 'items' => ['admin']],
|
'dashboard' => ['icon' => 'dashboard', 'items' => ['admin']],
|
||||||
'podcasts' => [
|
'podcasts' => [
|
||||||
'icon' => 'mic',
|
'icon' => 'mic',
|
||||||
'items' => ['my-podcasts', 'podcast-list', 'podcast-create'],
|
'items' => ['podcast-list', 'podcast-create'],
|
||||||
],
|
],
|
||||||
'users' => ['icon' => 'group', 'items' => ['user-list', 'user-create']],
|
'users' => ['icon' => 'group', 'items' => ['user-list', 'user-create']],
|
||||||
|
'pages' => ['icon' => 'pages', 'items' => ['page-list', 'page-create']],
|
||||||
]; ?>
|
]; ?>
|
||||||
|
|
||||||
<nav class="<?= $class ?>">
|
<nav class="<?= $class ?>">
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?= $this->extend('admin/_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Page.create') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
|
<?= form_open(route_to('page-create'), [
|
||||||
|
'class' => 'flex flex-col max-w-3xl',
|
||||||
|
]) ?>
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
|
<?= form_label(lang('Page.form.title'), 'title', ['class' => 'max-w-sm']) ?>
|
||||||
|
<?= form_input([
|
||||||
|
'id' => 'title',
|
||||||
|
'name' => 'title',
|
||||||
|
'class' => 'form-input mb-4 max-w-sm',
|
||||||
|
'value' => old('title'),
|
||||||
|
'required' => 'required',
|
||||||
|
'data-slugify' => 'title',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
<?= form_label(lang('Page.form.slug'), 'slug', ['class' => 'max-w-sm']) ?>
|
||||||
|
<?= form_input([
|
||||||
|
'id' => 'slug',
|
||||||
|
'name' => 'slug',
|
||||||
|
'class' => 'form-input mb-4 max-w-sm',
|
||||||
|
'value' => old('slug'),
|
||||||
|
'required' => 'required',
|
||||||
|
'data-slugify' => 'slug',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<?= form_label(lang('Page.form.content'), 'content') ?>
|
||||||
|
<?= form_textarea(
|
||||||
|
[
|
||||||
|
'id' => 'content',
|
||||||
|
'name' => 'content',
|
||||||
|
'class' => 'form-textarea',
|
||||||
|
'required' => 'required',
|
||||||
|
],
|
||||||
|
old('content', '', false),
|
||||||
|
'data-editor="markdown"'
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= form_button([
|
||||||
|
'content' => lang('Page.form.submit_create'),
|
||||||
|
'type' => 'submit',
|
||||||
|
'class' => 'self-end px-4 py-2 bg-gray-200',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
<?= form_close() ?>
|
||||||
|
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?= $this->extend('admin/_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Page.edit') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
|
<?= form_open(route_to('page-edit', $page->id), [
|
||||||
|
'class' => 'flex flex-col max-w-3xl',
|
||||||
|
]) ?>
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
|
<?= form_label(lang('Page.form.title'), 'title', ['class' => 'max-w-sm']) ?>
|
||||||
|
<?= form_input([
|
||||||
|
'id' => 'title',
|
||||||
|
'name' => 'title',
|
||||||
|
'class' => 'form-input mb-4 max-w-sm',
|
||||||
|
'value' => old('title', $page->title),
|
||||||
|
'required' => 'required',
|
||||||
|
'data-slugify' => 'title',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
<?= form_label(lang('Page.form.slug'), 'slug', ['class' => 'max-w-sm']) ?>
|
||||||
|
<?= form_input([
|
||||||
|
'id' => 'slug',
|
||||||
|
'name' => 'slug',
|
||||||
|
'class' => 'form-input mb-4 max-w-sm',
|
||||||
|
'value' => old('slug', $page->slug),
|
||||||
|
'required' => 'required',
|
||||||
|
'data-slugify' => 'slug',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<?= form_label(lang('Page.form.content'), 'content') ?>
|
||||||
|
<?= form_textarea(
|
||||||
|
[
|
||||||
|
'id' => 'content',
|
||||||
|
'name' => 'content',
|
||||||
|
'class' => 'form-textarea',
|
||||||
|
'required' => 'required',
|
||||||
|
],
|
||||||
|
old('content', $page->content, false),
|
||||||
|
'data-editor="markdown"'
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= form_button([
|
||||||
|
'content' => lang('Page.form.submit_edit'),
|
||||||
|
'type' => 'submit',
|
||||||
|
'class' => 'self-end px-4 py-2 bg-gray-200',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
<?= form_close() ?>
|
||||||
|
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?= $this->extend('admin/_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Page.all_pages') ?> (<?= count($pages) ?>)
|
||||||
|
<a class="inline-flex items-center px-2 py-1 mb-2 ml-4 text-sm text-white bg-green-500 rounded shadow-xs outline-none hover:bg-green-600 focus:shadow-outline" href="<?= route_to(
|
||||||
|
'page-create'
|
||||||
|
) ?>">
|
||||||
|
<?= icon('add', 'mr-2') ?>
|
||||||
|
<?= lang('Page.create') ?></a>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
|
<table class="table-auto">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="px-4 py-2">Title</th>
|
||||||
|
<th class="px-4 py-2">Slug</th>
|
||||||
|
<th class="px-4 py-2">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($pages as $page): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-2 border"><?= $page->title ?></td>
|
||||||
|
<td class="px-4 py-2 border"><?= $page->slug ?></td>
|
||||||
|
<td class="px-4 py-2 border">
|
||||||
|
<a class="inline-flex px-2 py-1 mb-2 text-sm text-white bg-gray-700 hover:bg-gray-800" href="<?= route_to(
|
||||||
|
'page',
|
||||||
|
$page->slug
|
||||||
|
) ?>"><?= lang('Page.go_to_page') ?></a>
|
||||||
|
<a class="inline-flex px-2 py-1 mb-2 text-sm text-white bg-teal-700 hover:bg-teal-800" href="<?= route_to(
|
||||||
|
'page-edit',
|
||||||
|
$page->id
|
||||||
|
) ?>"><?= lang('Page.edit') ?></a>
|
||||||
|
<a class="inline-flex px-2 py-1 text-sm text-white bg-red-700 hover:bg-red-800" href="<?= route_to(
|
||||||
|
'page-delete',
|
||||||
|
$page->id
|
||||||
|
) ?>"><?= lang('Page.delete') ?></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?= $this->extend('admin/_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= $page->title ?>
|
||||||
|
<a class="inline-flex items-center px-2 py-1 mb-2 ml-4 text-sm text-white bg-teal-500 rounded shadow-xs outline-none hover:bg-teal-600 focus:shadow-outline" href="<?= route_to(
|
||||||
|
'page-edit',
|
||||||
|
$page->id
|
||||||
|
) ?>">
|
||||||
|
<?= icon('edit', 'mr-2') ?>
|
||||||
|
<?= lang('Page.edit') ?></a>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
<div class="prose">
|
||||||
|
<?= $page->content_html ?>
|
||||||
|
</div>
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -54,6 +54,7 @@
|
||||||
[
|
[
|
||||||
'id' => 'episode_description_footer',
|
'id' => 'episode_description_footer',
|
||||||
'name' => 'episode_description_footer',
|
'name' => 'episode_description_footer',
|
||||||
|
|
||||||
'class' => 'form-textarea',
|
'class' => 'form-textarea',
|
||||||
],
|
],
|
||||||
old('episode_description_footer', '', false),
|
old('episode_description_footer', '', false),
|
||||||
|
@ -94,20 +95,13 @@
|
||||||
<span class="ml-2"><?= lang('Podcast.form.explicit') ?></span>
|
<span class="ml-2"><?= lang('Podcast.form.explicit') ?></span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<?= form_label(lang('Podcast.form.author'), 'author') ?>
|
|
||||||
<?= form_input([
|
|
||||||
'id' => 'author',
|
|
||||||
'name' => 'author',
|
|
||||||
'class' => 'form-input mb-4',
|
|
||||||
'value' => old('author'),
|
|
||||||
]) ?>
|
|
||||||
|
|
||||||
<?= form_label(lang('Podcast.form.owner_name'), 'owner_name') ?>
|
<?= form_label(lang('Podcast.form.owner_name'), 'owner_name') ?>
|
||||||
<?= form_input([
|
<?= form_input([
|
||||||
'id' => 'owner_name',
|
'id' => 'owner_name',
|
||||||
'name' => 'owner_name',
|
'name' => 'owner_name',
|
||||||
'class' => 'form-input mb-4',
|
'class' => 'form-input mb-4',
|
||||||
'value' => old('owner_name'),
|
'value' => old('owner_name'),
|
||||||
|
'required' => 'required',
|
||||||
]) ?>
|
]) ?>
|
||||||
|
|
||||||
<?= form_label(lang('Podcast.form.owner_email'), 'owner_email') ?>
|
<?= form_label(lang('Podcast.form.owner_email'), 'owner_email') ?>
|
||||||
|
@ -120,9 +114,15 @@
|
||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
]) ?>
|
]) ?>
|
||||||
|
|
||||||
<?= form_fieldset('', [
|
<?= form_label(lang('Podcast.form.author'), 'author') ?>
|
||||||
'class' => 'flex flex-col mb-4',
|
<?= form_input([
|
||||||
|
'id' => 'author',
|
||||||
|
'name' => 'author',
|
||||||
|
'class' => 'form-input mb-4',
|
||||||
|
'value' => old('author'),
|
||||||
]) ?>
|
]) ?>
|
||||||
|
|
||||||
|
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
|
||||||
<legend><?= lang('Podcast.form.type.label') ?></legend>
|
<legend><?= lang('Podcast.form.type.label') ?></legend>
|
||||||
<label for="episodic" class="inline-flex items-center">
|
<label for="episodic" class="inline-flex items-center">
|
||||||
<?= form_radio(
|
<?= form_radio(
|
||||||
|
|
|
@ -109,20 +109,13 @@
|
||||||
<span class="ml-2"><?= lang('Podcast.form.explicit') ?></span>
|
<span class="ml-2"><?= lang('Podcast.form.explicit') ?></span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<?= form_label(lang('Podcast.form.author'), 'author') ?>
|
|
||||||
<?= form_input([
|
|
||||||
'id' => 'author',
|
|
||||||
'name' => 'author',
|
|
||||||
'class' => 'form-input mb-4',
|
|
||||||
'value' => old('author', $podcast->author),
|
|
||||||
]) ?>
|
|
||||||
|
|
||||||
<?= form_label(lang('Podcast.form.owner_name'), 'owner_name') ?>
|
<?= form_label(lang('Podcast.form.owner_name'), 'owner_name') ?>
|
||||||
<?= form_input([
|
<?= form_input([
|
||||||
'id' => 'owner_name',
|
'id' => 'owner_name',
|
||||||
'name' => 'owner_name',
|
'name' => 'owner_name',
|
||||||
'class' => 'form-input mb-4',
|
'class' => 'form-input mb-4',
|
||||||
'value' => old('owner_name', $podcast->owner_name),
|
'value' => old('owner_name', $podcast->owner_name),
|
||||||
|
'required' => 'required',
|
||||||
]) ?>
|
]) ?>
|
||||||
|
|
||||||
<?= form_label(lang('Podcast.form.owner_email'), 'owner_email') ?>
|
<?= form_label(lang('Podcast.form.owner_email'), 'owner_email') ?>
|
||||||
|
@ -135,6 +128,14 @@
|
||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
]) ?>
|
]) ?>
|
||||||
|
|
||||||
|
<?= form_label(lang('Podcast.form.author'), 'author') ?>
|
||||||
|
<?= form_input([
|
||||||
|
'id' => 'author',
|
||||||
|
'name' => 'author',
|
||||||
|
'class' => 'form-input mb-4',
|
||||||
|
'value' => old('author', $podcast->author),
|
||||||
|
]) ?>
|
||||||
|
|
||||||
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
|
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
|
||||||
<legend><?= lang('Podcast.form.type.label') ?></legend>
|
<legend><?= lang('Podcast.form.type.label') ?></legend>
|
||||||
<label for="episodic" class="inline-flex items-center">
|
<label for="episodic" class="inline-flex items-center">
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<?= $this->extend('_layout') ?>
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= $episode->title ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
<a class="underline hover:no-underline" href="<?= route_to(
|
<a class="underline hover:no-underline" href="<?= route_to(
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?= $this->extend('_layout') ?>
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>Castopod<?= $this->endSection() ?>
|
||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
<h1 class="mb-2 text-xl"><?= lang('Home.all_podcasts') ?> (<?= count(
|
<h1 class="mb-2 text-xl"><?= lang('Home.all_podcasts') ?> (<?= count(
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= $page->title ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
<div class="prose">
|
||||||
|
<?= $page->content_html ?>
|
||||||
|
</div>
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -1,5 +1,9 @@
|
||||||
<?= $this->extend('_layout') ?>
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= $podcast->title ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
<header class="py-4 border-b">
|
<header class="py-4 border-b">
|
||||||
<h1 class="text-2xl"><?= $podcast->title ?></h1>
|
<h1 class="text-2xl"><?= $podcast->title ?></h1>
|
||||||
|
|
Loading…
Reference in New Issue