feat: edit + delete podcast and episode

- refactor model / entity and controller logic for DRY code
- update episodes and podcasts
migrations
- define callbacks for podcast and episode models for enclosure update and cache
clearing
This commit is contained in:
Yassine Doghri 2020-06-30 18:17:41 +02:00
parent c815ecd664
commit ac5f0c7328
24 changed files with 1120 additions and 87585 deletions

View File

@ -38,6 +38,12 @@ $routes->add('new-podcast', 'Podcast::create', ['as' => 'podcast_create']);
$routes->group('@(:podcastName)', function ($routes) {
$routes->add('/', 'Podcast::view/$1', ['as' => 'podcast_view']);
$routes->add('edit', 'Podcast::edit/$1', [
'as' => 'podcast_edit',
]);
$routes->add('delete', 'Podcast::delete/$1', [
'as' => 'podcast_delete',
]);
$routes->add('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
$routes->add('new-episode', 'Episode::create/$1', [
'as' => 'episode_create',
@ -45,6 +51,12 @@ $routes->group('@(:podcastName)', function ($routes) {
$routes->add('episodes/(:episodeSlug)', 'Episode::view/$1/$2', [
'as' => 'episode_view',
]);
$routes->add('episodes/(:episodeSlug)/edit', 'Episode::edit/$1/$2', [
'as' => 'episode_edit',
]);
$routes->add('episodes/(:episodeSlug)/delete', 'Episode::delete/$1/$2', [
'as' => 'episode_delete',
]);
});
// Route for podcast audio file analytics (/stats/podcast_id/episode_id/podcast_folder/filename.mp3)

View File

@ -10,23 +10,44 @@ namespace App\Controllers;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
helper('podcast');
class Episode extends BaseController
{
public function create($podcast_name)
{
helper(['form', 'database', 'media', 'id3']);
protected \App\Entities\Podcast $podcast;
protected ?\App\Entities\Episode $episode;
$episode_model = new EpisodeModel();
public function _remap($method, ...$params)
{
$podcast_model = new PodcastModel();
$podcast = $podcast_model->where('name', $podcast_name)->first();
$this->podcast = $podcast_model->where('name', $params[0])->first();
if (count($params) > 1) {
$episode_model = new EpisodeModel();
if (
!($episode = $episode_model
->where([
'podcast_id' => $this->podcast->id,
'slug' => $params[1],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
}
return $this->$method();
}
public function create()
{
helper(['form']);
if (
!$this->validate([
'episode_file' =>
'uploaded[episode_file]|ext_in[episode_file,mp3,m4a]',
'enclosure' => 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required',
'description' => 'required',
@ -34,58 +55,19 @@ class Episode extends BaseController
])
) {
$data = [
'podcast' => $podcast,
'podcast' => $this->podcast,
];
echo view('episode/create', $data);
} else {
$episode_slug = $this->request->getVar('slug');
$episode_file = $this->request->getFile('episode_file');
$episode_file_metadata = get_file_tags($episode_file);
$image = $this->request->getFile('image');
// By default, the episode's image path is set to the podcast's
$image_path = $podcast->image_uri;
// check whether the user has inputted an image and store it
if ($image->isValid()) {
$image_path = save_podcast_media(
$image,
$podcast_name,
$episode_slug
);
} elseif ($APICdata = $episode_file_metadata['attached_picture']) {
// if the user didn't input an image,
// check if the uploaded audio file has an attached cover and store it
$cover_image = new \CodeIgniter\Files\File('episode_cover');
file_put_contents($cover_image, $APICdata);
$image_path = save_podcast_media(
$cover_image,
$podcast_name,
$episode_slug
);
}
$episode_path = save_podcast_media(
$episode_file,
$podcast_name,
$episode_slug
);
$episode = new \App\Entities\Episode([
'podcast_id' => $podcast->id,
$new_episode = new \App\Entities\Episode([
'podcast_id' => $this->podcast->id,
'title' => $this->request->getVar('title'),
'slug' => $episode_slug,
'enclosure_uri' => $episode_path,
'enclosure_length' => $episode_file->getSize(),
'enclosure_type' => $episode_file_metadata['mime_type'],
'slug' => $this->request->getVar('slug'),
'enclosure' => $this->request->getFile('enclosure'),
'pub_date' => $this->request->getVar('pub_date'),
'description' => $this->request->getVar('description'),
'duration' => $episode_file_metadata['playtime_seconds'],
'image_uri' => $image_path,
'image' => $this->request->getFile('image'),
'explicit' => $this->request->getVar('explicit') or false,
'number' => $this->request->getVar('episode_number'),
'season_number' => $this->request->getVar('season_number')
@ -97,30 +79,107 @@ class Episode extends BaseController
'block' => $this->request->getVar('block') or false,
]);
$episode_model->save($episode);
$episode_file = write_file_tags($podcast, $episode);
$episode_model = new EpisodeModel();
$episode_model->save($new_episode);
return redirect()->to(
base_url(route_to('episode_view', $podcast_name, $episode_slug))
base_url(
route_to(
'episode_view',
$this->podcast->name,
$new_episode->slug
)
)
);
}
}
public function view($podcast_name, $episode_slug)
public function edit()
{
$podcast_model = new PodcastModel();
$episode_model = new EpisodeModel();
helper(['form']);
$podcast = $podcast_model->where('name', $podcast_name)->first();
$episode = $episode_model->where('slug', $episode_slug)->first();
if (
!$this->validate([
'enclosure' =>
'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]|permit_empty',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
echo view('episode/edit', $data);
} else {
$this->episode->title = $this->request->getVar('title');
$this->episode->slug = $this->request->getVar('slug');
$this->episode->pub_date = $this->request->getVar('pub_date');
$this->episode->description = $this->request->getVar('description');
$this->episode->explicit =
($this->request->getVar('explicit') or false);
$this->episode->number = $this->request->getVar('episode_number');
$this->episode->season_number = $this->request->getVar(
'season_number'
)
? $this->request->getVar('season_number')
: null;
$this->episode->type = $this->request->getVar('type');
$this->episode->author_name = $this->request->getVar('author_name');
$this->episode->author_email = $this->request->getVar(
'author_email'
);
$this->episode->block = ($this->request->getVar('block') or false);
$enclosure = $this->request->getFile('enclosure');
if ($enclosure->isValid()) {
$this->episode->enclosure = $this->request->getFile(
'enclosure'
);
}
$image = $this->request->getFile('image');
if ($image) {
$this->episode->image = $this->request->getFile('image');
}
$episode_model = new EpisodeModel();
$episode_model->save($this->episode);
return redirect()->to(
base_url(
route_to(
'episode_view',
$this->podcast->name,
$this->episode->slug
)
)
);
}
}
public function view()
{
self::triggerWebpageHit($this->podcast->id);
$data = [
'podcast' => $podcast,
'episode' => $episode,
'podcast' => $this->podcast,
'episode' => $this->episode,
];
self::triggerWebpageHit($data['podcast']->id);
return view('episode/view', $data);
}
return view('episode/view.php', $data);
public function delete()
{
$episode_model = new EpisodeModel();
$episode_model->delete($this->episode->id);
return redirect()->to(
base_url(route_to('podcast_view', $this->podcast->name))
);
}
}

View File

@ -7,15 +7,26 @@
namespace App\Controllers;
use App\Models\CategoryModel;
use App\Models\EpisodeModel;
use App\Models\LanguageModel;
use App\Models\PodcastModel;
class Podcast extends BaseController
{
protected ?\App\Entities\Podcast $podcast;
public function _remap($method, ...$params)
{
if (count($params) > 0) {
$podcast_model = new PodcastModel();
$this->podcast = $podcast_model->where('name', $params[0])->first();
}
return $this->$method();
}
public function create()
{
helper(['form', 'database', 'media', 'misc']);
helper(['form', 'misc']);
$podcast_model = new PodcastModel();
if (
@ -24,8 +35,8 @@ class Podcast extends BaseController
'name' => 'required|regex_match[^[a-z0-9\_]{1,191}$]',
'description' => 'required|max_length[4000]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|max_dims[image,3000,3000]',
'owner_email' => 'required|valid_email|permit_empty',
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]',
'owner_email' => 'required|valid_email',
'type' => 'required',
])
) {
@ -41,18 +52,14 @@ class Podcast extends BaseController
echo view('podcast/create', $data);
} else {
$image = $this->request->getFile('image');
$podcast_name = $this->request->getVar('name');
$image_path = save_podcast_media($image, $podcast_name, 'cover');
$podcast = new \App\Entities\Podcast([
'title' => $this->request->getVar('title'),
'name' => $podcast_name,
'name' => $this->request->getVar('name'),
'description' => $this->request->getVar('description'),
'episode_description_footer' => $this->request->getVar(
'episode_description_footer'
),
'image_uri' => $image_path,
'image' => $this->request->getFile('image'),
'language' => $this->request->getVar('language'),
'category' => $this->request->getVar('category'),
'explicit' => $this->request->getVar('explicit') or false,
@ -77,20 +84,86 @@ class Podcast extends BaseController
}
}
public function view($podcast_name)
public function edit()
{
$podcast_model = new PodcastModel();
$episode_model = new EpisodeModel();
helper(['form', 'misc']);
if (
!$this->validate([
'title' => 'required',
'name' => 'required|regex_match[^[a-z0-9\_]{1,191}$]',
'description' => 'required|max_length[4000]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'owner_email' => 'required|valid_email',
'type' => 'required',
])
) {
$languageModel = new LanguageModel();
$categoryModel = new CategoryModel();
$data = [
'podcast' => $this->podcast,
'languages' => $languageModel->findAll(),
'categories' => $categoryModel->findAll(),
];
echo view('podcast/edit', $data);
} else {
$this->podcast->title = $this->request->getVar('title');
$this->podcast->name = $this->request->getVar('name');
$this->podcast->description = $this->request->getVar('description');
$this->podcast->episode_description_footer = $this->request->getVar(
'episode_description_footer'
);
$image = $this->request->getFile('image');
if ($image->isValid()) {
$this->podcast->image = $this->request->getFile('image');
}
$this->podcast->language = $this->request->getVar('language');
$this->podcast->category = $this->request->getVar('category');
$this->podcast->explicit =
($this->request->getVar('explicit') or false);
$this->podcast->author_name = $this->request->getVar('author_name');
$this->podcast->author_email = $this->request->getVar(
'author_email'
);
$this->podcast->owner_name = $this->request->getVar('owner_name');
$this->podcast->owner_email = $this->request->getVar('owner_email');
$this->podcast->type = $this->request->getVar('type');
$this->podcast->copyright = $this->request->getVar('copyright');
$this->podcast->block = ($this->request->getVar('block') or false);
$this->podcast->complete =
($this->request->getVar('complete') or false);
$this->podcast->custom_html_head = $this->request->getVar(
'custom_html_head'
);
$podcast_model = new PodcastModel();
$podcast_model->save($this->podcast);
return redirect()->to(
base_url(route_to('podcast_view', $this->podcast->name))
);
}
}
public function view()
{
self::triggerWebpageHit($this->podcast->id);
$podcast = $podcast_model->where('name', $podcast_name)->first();
$data = [
'podcast' => $podcast,
'episodes' => $episode_model
->where('podcast_id', $podcast->id)
->findAll(),
'podcast' => $this->podcast,
'episodes' => $this->podcast->episodes,
];
self::triggerWebpageHit($podcast->id);
return view('podcast/view', $data);
}
public function delete()
{
$podcast_model = new PodcastModel();
$podcast_model->delete($this->podcast->id);
return redirect()->to(base_url(route_to('home')));
}
}

View File

@ -141,6 +141,10 @@ class AddPodcasts extends Migration
'updated_at' => [
'type' => 'TIMESTAMP',
],
'deleted_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('podcasts');

View File

@ -47,25 +47,6 @@ class AddEpisodes extends Migration
'comment' =>
'The URI attribute points to your podcast media file. The file extension specified within the URI attribute determines whether or not content appears in the podcast directory. Supported file formats include M4A, MP3, MOV, MP4, M4V, and PDF.',
],
'enclosure_length' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'comment' =>
'The length attribute is the file size in bytes. You can find this information in the properties of your podcast file (on a Mac, choose File > Get Info and refer to the size field).',
],
'enclosure_type' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'comment' =>
'The type attribute provides the correct category for the type of file you are using. The type values for the supported file formats are: audio/x-m4a, audio/mpeg, video/quicktime, video/mp4, video/x-m4v, and application/pdf.',
],
'guid' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'comment' =>
'The episodes globally unique identifier (GUID). It is very important that each episode have a unique GUID and that it never changes, even if an episodes metadata, like title or enclosure URL, do change. Globally unique identifiers (GUID) are case-sensitive strings. If a GUID is not provided an episodes enclosure URL will be used instead. If a GUID is not provided, make sure that an episodes enclosure URL is unique and never changes. Failing to comply with these guidelines may result in duplicate episodes being shown to listeners, inaccurate data in Podcast Analytics, and can cause issues with your podcastss listing and chart placement in Apple Podcasts.',
],
'pub_date' => [
'type' => 'DATETIME',
'comment' =>
@ -87,6 +68,7 @@ class AddEpisodes extends Migration
'image_uri' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'null' => true,
'comment' =>
'The artwork for the show. Specify your show artwork by providing a URL linking to it. Depending on their device, subscribers see your podcast artwork in varying sizes. Therefore, make sure your design is effective at both its original size and at thumbnail size. You should include a show title, brand, or source name as part of your podcast artwork. Artwork must be a minimum size of 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels, in JPEG or PNG format, 72 dpi, with appropriate file extensions (.jpg, .png), and in the RGB colorspace.',
],
@ -146,6 +128,10 @@ class AddEpisodes extends Migration
'updated_at' => [
'type' => 'TIMESTAMP',
],
'deleted_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['podcast_id', 'slug']);

View File

@ -12,24 +12,24 @@ use CodeIgniter\Entity;
class Episode extends Entity
{
protected $link;
protected $image_media_path;
protected $image_url;
protected $enclosure_media_path;
protected $enclosure_url;
protected $guid;
protected $podcast;
protected \App\Entities\Podcast $podcast;
protected string $GUID;
protected string $link;
protected \CodeIgniter\Files\File $image;
protected string $image_media_path;
protected string $image_url;
protected \CodeIgniter\Files\File $enclosure;
protected string $enclosure_media_path;
protected string $enclosure_url;
protected array $enclosure_metadata;
protected $casts = [
'slug' => 'string',
'title' => 'string',
'enclosure_uri' => 'string',
'enclosure_length' => 'integer',
'enclosure_type' => 'string',
'pub_date' => 'datetime',
'description' => 'string',
'duration' => 'integer',
'image_uri' => 'string',
'image_uri' => '?string',
'author_name' => '?string',
'author_email' => '?string',
'explicit' => 'boolean',
@ -39,6 +39,38 @@ class Episode extends Entity
'block' => 'boolean',
];
public function setImage(?\CodeIgniter\HTTP\Files\UploadedFile $image)
{
if ($image->isValid()) {
// check whether the user has inputted an image and store it
$this->attributes['image_uri'] = save_podcast_media(
$image,
$this->getPodcast()->name,
$this->attributes['slug']
);
} elseif (
$APICdata = $this->getEnclosureMetadata()['attached_picture']
) {
// if the user didn't input an image,
// check if the uploaded audio file has an attached cover and store it
$cover_image = new \CodeIgniter\Files\File('episode_cover');
file_put_contents($cover_image, $APICdata);
$this->attributes['image_uri'] = save_podcast_media(
$cover_image,
$this->getPodcast()->name,
$this->attributes['slug']
);
}
return $this;
}
public function getImage()
{
return new \CodeIgniter\Files\File($this->getImageMediaPath());
}
public function getImageMediaPath()
{
return media_path($this->attributes['image_uri']);
@ -46,11 +78,37 @@ class Episode extends Entity
public function getImageUrl()
{
return media_url($this->attributes['image_uri']);
if ($image_uri = $this->attributes['image_uri']) {
return media_url($image_uri);
}
return $this->getPodcast()->image_url;
}
public function setEnclosure(
\CodeIgniter\HTTP\Files\UploadedFile $enclosure = null
) {
if ($enclosure->isValid()) {
helper('media');
$this->attributes['enclosure_uri'] = save_podcast_media(
$enclosure,
$this->getPodcast()->name,
$this->attributes['slug']
);
return $this;
}
}
public function getEnclosure()
{
return new \CodeIgniter\Files\File($this->getEnclosureMediaPath());
}
public function getEnclosureMediaPath()
{
helper('media');
return media_path($this->attributes['enclosure_uri']);
}
@ -66,6 +124,13 @@ class Episode extends Entity
);
}
public function getEnclosureMetadata()
{
helper('id3');
return get_file_tags($this->getEnclosure());
}
public function getLink()
{
return base_url(
@ -77,7 +142,7 @@ class Episode extends Entity
);
}
public function getGuid()
public function getGUID()
{
return $this->getLink();
}

View File

@ -13,6 +13,8 @@ use CodeIgniter\Entity;
class Podcast extends Entity
{
protected $link;
protected \CodeIgniter\Files\File $image;
protected $image_media_path;
protected $image_url;
protected $episodes;
@ -37,6 +39,31 @@ class Podcast extends Entity
'custom_html_head' => '?string',
];
public function setImage(\CodeIgniter\HTTP\Files\UploadedFile $image = null)
{
if ($image) {
helper('media');
$this->attributes['image_uri'] = save_podcast_media(
$image,
$this->attributes['name'],
'cover'
);
return $this;
}
}
public function getImage()
{
return new \CodeIgniter\Files\File($this->getImageMediaPath());
}
public function getImageMediaPath()
{
return media_path($this->attributes['image_uri']);
}
public function getImageUrl()
{
return media_url($this->attributes['image_uri']);

View File

@ -142,7 +142,7 @@ function set_user_session_referer()
}
}
function webpage_hit($postcast_id)
function webpage_hit($podcast_id)
{
$session = \Config\Services::session();
$session->start();
@ -150,7 +150,7 @@ function webpage_hit($postcast_id)
$procedureName = $db->prefixTable('analytics_website');
$db->query("call $procedureName(?,?,?,?)", [
$postcast_id,
$podcast_id,
$session->get('country'),
$session->get('browser'),
$session->get('referer'),

View File

@ -33,12 +33,11 @@ function get_file_tags($file)
/**
* Write audio file metadata / ID3 tags
*
* @param App\Entities\Podcast $podcast
* @param App\Entities\Episode $episode
*
* @return UploadedFile
*/
function write_file_tags($podcast, $episode)
function write_enclosure_tags($episode)
{
$TextEncoding = 'UTF-8';
@ -55,20 +54,20 @@ function write_file_tags($podcast, $episode)
$APICdata = file_get_contents($cover->getRealPath());
// TODO: variables used for podcast specific tags
// $podcast_url = $podcast->link;
// $podcast_feed_url = $podcast->feed_url;
// $podcast_url = $episode->podcast->link;
// $podcast_feed_url = $episode->podcast->feed_url;
// $episode_media_url = $episode->link;
// populate data array
$TagData = [
'title' => [$episode->title],
'artist' => [$podcast->author],
'album' => [$podcast->title],
'artist' => [$episode->podcast->author],
'album' => [$episode->podcast->title],
'year' => [$episode->pub_date->format('Y')],
'genre' => ['Podcast'],
'comment' => [$episode->description],
'track_number' => [strval($episode->number)],
'copyright_message' => [$podcast->copyright],
'copyright_message' => [$episode->podcast->copyright],
'publisher' => ['Podlibre'],
'encoded_by' => ['Castopod'],

View File

@ -116,14 +116,20 @@ function get_rss_feed($podcast)
$item = $channel->addChild('item');
$item->addChild('title', $episode->title);
$enclosure = $item->addChild('enclosure');
$enclosure_metadata = $episode->enclosure_metadata;
$enclosure->addAttribute('url', $episode->enclosure_url);
$enclosure->addAttribute('length', $episode->enclosure_length);
$enclosure->addAttribute('type', $episode->enclosure_type);
$enclosure->addAttribute('length', $enclosure_metadata['filesize']);
$enclosure->addAttribute('type', $enclosure_metadata['mime_type']);
$item->addChild('guid', $episode->guid);
$item->addChild('pubDate', $episode->pub_date->format(DATE_RFC1123));
$item->addChildWithCDATA('description', $episode->description);
$item->addChild('duration', $episode->duration, $itunes_namespace);
$item->addChild(
'duration',
$enclosure_metadata['playtime_seconds'],
$itunes_namespace
);
$item->addChild('link', $episode->link);
$episode_itunes_image = $item->addChild(
'image',

View File

@ -2,6 +2,8 @@
return [
'back_to_podcast' => 'Go back to podcast',
'edit' => 'Edit',
'delete' => 'Delete',
'create' => 'Add an episode',
'form' => [
'file' => 'Audio file',
@ -22,6 +24,7 @@ return [
'episode_number' => 'Episode number',
'season_number' => 'Season number',
'block' => 'Block',
'submit' => 'Create episode',
'submit_create' => 'Create episode',
'submit_edit' => 'Save episode',
]
];

View File

@ -2,6 +2,10 @@
return [
'create' => 'Create a Podcast',
'new_episode' => 'New Episode',
'feed' => 'RSS feed',
'edit' => 'Edit',
'delete' => 'Delete',
'form' => [
'title' => 'Title',
'name' => 'Name',
@ -24,7 +28,8 @@ return [
'block' => 'Block',
'complete' => 'Complete',
'custom_html_head' => 'Custom HTML code in <head/>',
'submit' => 'Create podcast',
'submit_create' => 'Create podcast',
'submit_edit' => 'Save podcast',
],
'category_options' => [
'uncategorized' => 'uncategorized',

View File

@ -35,7 +35,40 @@ class EpisodeModel extends Model
];
protected $returnType = 'App\Entities\Episode';
protected $useSoftDeletes = false;
protected $useSoftDeletes = true;
protected $useTimestamps = true;
protected $afterInsert = [
'writeEnclosureMetadata',
'clearPodcastFeedCache',
];
protected $afterUpdate = [
'writeEnclosureMetadata',
'clearPodcastFeedCache',
];
protected function writeEnclosureMetadata(array $data)
{
helper('id3');
$episode = $this->find(
is_array($data['id']) ? $data['id'][0] : $data['id']
);
write_enclosure_tags($episode);
return $data;
}
protected function clearPodcastFeedCache(array $data)
{
$episode = $this->find(
is_array($data['id']) ? $data['id'][0] : $data['id']
);
$cache = \Config\Services::cache();
$cache->delete(md5($episode->podcast->feed_url));
}
}

View File

@ -36,7 +36,21 @@ class PodcastModel extends Model
];
protected $returnType = 'App\Entities\Podcast';
protected $useSoftDeletes = false;
protected $useSoftDeletes = true;
protected $useTimestamps = true;
protected $afterInsert = ['clearPodcastFeedCache'];
protected $afterUpdate = ['clearPodcastFeedCache'];
protected function clearPodcastFeedCache(array $data)
{
$podcast = $this->find(
is_array($data['id']) ? $data['id'][0] : $data['id']
);
$cache = \Config\Services::cache();
$cache->delete(md5($podcast->feed_url));
}
}

View File

@ -15,8 +15,8 @@
<?= csrf_field() ?>
<div class="flex flex-col mb-4">
<label for="episode_file"><?= lang('Episode.form.file') ?></label>
<input type="file" class="form-input" id="episode_file" name="episode_file" required accept=".mp3,.m4a" />
<label for="enclosure"><?= lang('Episode.form.file') ?></label>
<input type="file" class="form-input" id="enclosure" name="enclosure" required accept=".mp3,.m4a" />
</div>
<div class="flex flex-col mb-4">
@ -97,7 +97,7 @@
</div>
<button type="submit" name="submit" class="self-end px-4 py-2 bg-gray-200"><?= lang(
'Episode.form.submit'
'Episode.form.submit_create'
) ?></button>
<?= form_close() ?>

115
app/Views/episode/edit.php Normal file
View File

@ -0,0 +1,115 @@
<?= $this->extend('layouts/default') ?>
<?= $this->section('content') ?>
<h1 class="mb-6 text-xl"><?= lang('Episode.edit') ?></h1>
<div class="mb-8">
<?= \Config\Services::validation()->listErrors() ?>
</div>
<?= form_open_multipart(
route_to('episode_edit', $podcast->name, $episode->slug),
[
'method' => 'post',
'class' => 'flex flex-col max-w-md',
]
) ?>
<?= csrf_field() ?>
<div class="flex flex-col mb-4">
<label for="enclosure"><?= lang('Episode.form.file') ?></label>
<input type="file" class="form-input" id="enclosure" name="enclosure" accept=".mp3,.m4a" />
</div>
<div class="flex flex-col mb-4">
<label for="title"><?= lang('Episode.form.title') ?></label>
<input type="text" class="form-input" id="title" name="title" value="<?= $episode->title ?>" required />
</div>
<div class="flex flex-col mb-4">
<label for="slug"><?= lang('Episode.form.slug') ?></label>
<input type="text" class="form-input" id="slug" name="slug" value="<?= $episode->slug ?>" required />
</div>
<div class="flex flex-col mb-4">
<label for="description"><?= lang('Episode.form.description') ?></label>
<textarea class="form-textarea" id="description" name="description" required><?= $episode->description ?></textarea>
</div>
<div class="flex flex-col mb-4">
<label for="pub_date"><?= lang('Episode.form.pub_date') ?></label>
<input type="date" class="form-input" id="pub_date" name="pub_date" value="<?= $episode->pub_date->format(
'Y-m-d'
) ?>" />
</div>
<div class="flex flex-col mb-4">
<label for="image"><?= lang('Episode.form.image') ?></label>
<input type="file" class="form-input" id="image" name="image" accept=".jpg,.jpeg,.png" />
</div>
<div class="flex flex-col mb-4">
<label for="episode_number"><?= lang(
'Episode.form.episode_number'
) ?></label>
<input type="number" class="form-input" id="episode_number" name="episode_number" value="<?= $episode->number ?>" required />
</div>
<div class="flex flex-col mb-4">
<label for="season_number"><?= lang('Episode.form.season_number') ?></label>
<input type="number" class="form-input" id="season_number" name="season_number" value="<?= $episode->season_number ?>" />
</div>
<div class="inline-flex items-center mb-4">
<input type="checkbox" id="explicit" name="explicit" class="form-checkbox" <?= $episode->explicit
? 'checked'
: '' ?> />
<label for="explicit" class="pl-2"><?= lang(
'Episode.form.explicit'
) ?></label>
</div>
<div class="flex flex-col mb-4">
<label for="author_name"><?= lang('Podcast.form.author_name') ?></label>
<input type="text" class="form-input" id="author_name" name="author_name" value="<?= $episode->author_name ?>" />
</div>
<div class="flex flex-col mb-4">
<label for="author_email"><?= lang('Podcast.form.author_email') ?></label>
<input type="email" class="form-input" id="author_email" name="author_email" value="<?= $episode->author_email ?>" />
</div>
<fieldset class="flex flex-col mb-4">
<legend><?= lang('Episode.form.type.label') ?></legend>
<label for="full" class="inline-flex items-center">
<input type="radio" class="form-radio" value="full" id="full" name="type" required
<?= $episode->type == 'full' ? 'checked' : '' ?>/>
<span class="ml-2"><?= lang('Episode.form.type.full') ?></span>
</label>
<label for="trailer" class="inline-flex items-center">
<input type="radio" class="form-radio" value="trailer" id="trailer" name="type" required
<?= $episode->type == 'trailer' ? 'checked' : '' ?>/>
<span class="ml-2"><?= lang('Episode.form.type.trailer') ?></span>
</label>
<label for="bonus" class="inline-flex items-center">
<input type="radio" class="form-radio" value="bonus" id="bonus" name="type" required
<?= $episode->type == 'bonus' ? 'checked' : '' ?> />
<span class="ml-2"><?= lang('Episode.form.type.bonus') ?></span>
</label>
</fieldset>
<div class="inline-flex items-center mb-4">
<input type="checkbox" id="block" name="block" class="form-checkbox" <?= $episode->block
? 'checked'
: '' ?> />
<label for="block" class="pl-2"><?= lang('Episode.form.block') ?></label>
</div>
<button type="submit" name="submit" class="self-end px-4 py-2 bg-gray-200"><?= lang(
'Episode.form.submit_edit'
) ?></button>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -8,9 +8,23 @@
) ?>">< <?= lang('Episode.back_to_podcast') ?></a>
<h1 class="text-2xl font-semibold"><?= $episode->title ?></h1>
<img src="<?= $episode->image_url ?>" alt="Episode cover" class="object-cover w-40 h-40 mb-6" />
<audio controls preload="none">
<audio controls preload="none" class="mb-12">
<source src="<?= $episode->enclosure_url ?>" type="<?= $episode->enclosure_type ?>">
Your browser does not support the audio tag.
</audio>
<a class="inline-flex px-4 py-2 text-white bg-teal-700 hover:bg-teal-800" href="<?= route_to(
'episode_edit',
$podcast->name,
$episode->slug
) ?>"><?= lang('Episode.edit') ?></a>
<a href="<?= route_to(
'episode_delete',
$podcast->name,
$episode->slug
) ?>" class="inline-flex px-4 py-2 text-white bg-red-700 hover:bg-red-800"><?= lang(
'Episode.delete'
) ?></a>
<?= $this->endSection() ?>

View File

@ -9,7 +9,7 @@
<?php if ($podcasts): ?>
<?php foreach ($podcasts as $podcast): ?>
<a href="<?= route_to('podcast_view', $podcast->name) ?>">
<article class="w-48 p-2 mb-4 mr-4 border shadow-sm hover:bg-gray-100 hover:shadow">
<article class="w-48 h-full p-2 mb-4 mr-4 border shadow-sm hover:bg-gray-100 hover:shadow">
<img alt="<?= $podcast->title ?>" src="<?= $podcast->image_url ?>" class="object-cover w-full h-40 mb-2" />
<h2 class="font-semibold leading-tight"><?= $podcast->title ?></h2>
<p class="text-gray-600">@<?= $podcast->name ?></p>

File diff suppressed because it is too large Load Diff

View File

@ -130,7 +130,7 @@
</div>
<button type="submit" name="submit" class="self-end px-4 py-2 bg-gray-200"><?= lang(
'Podcast.form.submit'
'Podcast.form.submit_create'
) ?></button>
<?= form_close() ?>

145
app/Views/podcast/edit.php Normal file
View File

@ -0,0 +1,145 @@
<?= $this->extend('layouts/default') ?>
<?= $this->section('content') ?>
<h1 class="mb-6 text-xl"><?= lang('Podcast.edit') ?></h1>
<div class="mb-8">
<?= \Config\Services::validation()->listErrors() ?>
</div>
<?= form_open_multipart(base_url(route_to('podcast_edit', $podcast->name)), [
'method' => 'post',
'class' => 'flex flex-col max-w-md',
]) ?>
<?= csrf_field() ?>
<div class="flex flex-col mb-4">
<label for="title"><?= lang('Podcast.form.title') ?></label>
<input type="text" class="form-input" id="title" name="title" value="<?= $podcast->title ?>" required />
</div>
<div class="flex flex-col mb-4">
<label for="name"><?= lang('Podcast.form.name') ?></label>
<input type="text" class="form-input" id="name" name="name" value="<?= $podcast->name ?>" required />
</div>
<div class="flex flex-col mb-4">
<label for="description"><?= lang('Podcast.form.description') ?></label>
<textarea class="form-textarea" id="description" name="description" required><?= $podcast->description ?></textarea>
</div>
<div class="flex flex-col mb-4">
<label for="episode_description_footer"><?= lang(
'Podcast.form.episode_description_footer'
) ?></label>
<textarea class="form-textarea" id="episode_description_footer" name="episode_description_footer"><?= $podcast->episode_description_footer ?></textarea>
</div>
<div class="flex flex-col mb-4">
<label for="image"><?= lang('Podcast.form.image') ?></label>
<input type="file" class="form-input" id="image" name="image" />
<img src="<?= $podcast->image_url ?>" alt="<?= $podcast->title ?>" class="object-cover w-32 h-32" />
</div>
<div class="flex flex-col mb-4">
<label for="language"><?= lang('Podcast.form.language') ?></label>
<select id="language" name="language" autocomplete="off" class="form-select" required>
<?php foreach ($languages as $language): ?>
<option <?= $podcast->language == $language->code
? "selected='selected'"
: '' ?> value="<?= $language->code ?>">
<?= $language->native_name ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-col mb-4">
<label for="category"><?= lang('Podcast.form.category') ?></label>
<select id="category" name="category" class="form-select" required>
<?php foreach ($categories as $category): ?>
<option <?= $podcast->category == $category->code
? "selected='selected'"
: '' ?> value="<?= $category->code ?>"><?= lang(
'Podcast.category_options.' . $category->code
) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="inline-flex items-center mb-4">
<input type="checkbox" id="explicit" name="explicit" class="form-checkbox" checked="<?= $podcast->explicit ?>" />
<label for="explicit" class="pl-2"><?= lang(
'Podcast.form.explicit'
) ?></label>
</div>
<div class="flex flex-col mb-4">
<label for="author_name"><?= lang('Podcast.form.author_name') ?></label>
<input type="text" class="form-input" id="author_name" name="author_name" value="<?= $podcast->author_name ?>" />
</div>
<div class="flex flex-col mb-4">
<label for="author_email"><?= lang('Podcast.form.author_email') ?></label>
<input type="email" class="form-input" id="author_email" name="author_email" value="<?= $podcast->author_email ?>" />
</div>
<div class="flex flex-col mb-4">
<label for="owner_name"><?= lang('Podcast.form.owner_name') ?></label>
<input type="text" class="form-input" id="owner_name" name="owner_name" value="<?= $podcast->owner_name ?>" />
</div>
<div class="flex flex-col mb-4">
<label for="owner_email"><?= lang('Podcast.form.owner_email') ?></label>
<input type="email" class="form-input" id="owner_email" name="owner_email" value="<?= $podcast->owner_email ?>" required />
</div>
<fieldset class="flex flex-col mb-4">
<legend><?= lang('Podcast.form.type.label') ?></legend>
<label for="episodic" class="inline-flex items-center">
<input type="radio" class="form-radio" value="episodic" id="episodic" name="type" required
<?= $podcast->type == 'episodic' ? 'checked' : '' ?> />
<span class="ml-2"><?= lang('Podcast.form.type.episodic') ?></span>
</label>
<label for="serial" class="inline-flex items-center">
<input type="radio" class="form-radio" value="serial" id="serial" name="type" required
<?= $podcast->type == 'serial' ? 'checked' : '' ?>/>
<span class="ml-2"><?= lang('Podcast.form.type.serial') ?></span>
</label>
</fieldset>
<div class="flex flex-col mb-4">
<label for="copyright"><?= lang('Podcast.form.copyright') ?></label>
<input type="text" class="form-input" id="copyright" name="copyright" value="<?= $podcast->copyright ?>" />
</div>
<div class="inline-flex items-center mb-4">
<input type="checkbox" id="block" name="block" class="form-checkbox"
<?= $podcast->block ? 'checked' : '' ?> />
<label for="block" class="pl-2"><?= lang('Podcast.form.block') ?></label>
</div>
<div class="inline-flex items-center mb-4">
<input type="checkbox" id="complete" name="complete" class="form-checkbox"
<?= $podcast->block ? 'checked' : '' ?> />
<label for="complete" class="pl-2"><?= lang(
'Podcast.form.complete'
) ?></label>
</div>
<div class="flex flex-col mb-4">
<label for="custom_html_head"><?= esc(
lang('Podcast.form.custom_html_head')
) ?></label>
<textarea class="form-textarea" id="custom_html_head" name="custom_html_head"><?= $podcast->custom_html_head ?></textarea>
</div>
<button type="submit" name="submit" class="self-end px-4 py-2 bg-gray-200"><?= lang(
'Podcast.form.submit_edit'
) ?></button>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -3,15 +3,23 @@
<?= $this->section('content') ?>
<header class="py-4 border-b">
<h1 class="text-2xl"><?= $podcast->title ?></h1>
<img src="<?= $podcast->image_url ?>" alt="Podcast cover" class="w-40 h-40 mb-6" />
<img src="<?= $podcast->image_url ?>" alt="Podcast cover" class="object-cover w-40 h-40 mb-6" />
<a class="inline-flex px-4 py-2 border hover:bg-gray-100" href="<?= route_to(
'episode_create',
$podcast->name
) ?>">New Episode</a>
) ?>"><?= lang('Podcast.new_episode') ?></a>
<a class="inline-flex px-4 py-2 bg-orange-500 hover:bg-orange-600" href="<?= route_to(
'podcast_feed',
$podcast->name
) ?>">RSS feed</a>
) ?>"><?= lang('Podcast.feed') ?></a>
<a class="inline-flex px-4 py-2 text-white bg-teal-700 hover:bg-teal-800" href="<?= route_to(
'podcast_edit',
$podcast->name
) ?>"><?= lang('Podcast.edit') ?></a>
<a class="inline-flex px-4 py-2 text-white bg-red-700 hover:bg-red-800" href="<?= route_to(
'podcast_delete',
$podcast->name
) ?>"><?= lang('Podcast.delete') ?></a>
</header>
<section class="flex flex-col py-4">

671
package-lock.json generated
View File

@ -52,214 +52,137 @@
}
}
},
"@commitlint/cli": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-8.3.5.tgz",
"integrity": "sha512-6+L0vbw55UEdht71pgWOE55SRgb+8OHcEwGDB234VlIBFGK9P2QOBU7MHiYJ5cjdjCQ0rReNrGjOHmJ99jwf0w==",
"@babel/runtime": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz",
"integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==",
"dev": true,
"requires": {
"@commitlint/format": "^8.3.4",
"@commitlint/lint": "^8.3.5",
"@commitlint/load": "^8.3.5",
"@commitlint/read": "^8.3.4",
"babel-polyfill": "6.26.0",
"chalk": "2.4.2",
"regenerator-runtime": "^0.13.4"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
"dev": true
}
}
},
"@commitlint/cli": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-9.0.1.tgz",
"integrity": "sha512-BVOc/BY0FMmKTTH5oUVE0ukhPWDFf364FiYKk3GlXLOGTZPTXQ/9ncB2eMOaCF0PdcEVY4VoMjyoRSgcVapCMg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.6",
"@commitlint/format": "^9.0.1",
"@commitlint/lint": "^9.0.1",
"@commitlint/load": "^9.0.1",
"@commitlint/read": "^9.0.1",
"chalk": "3.0.0",
"core-js": "^3.6.1",
"get-stdin": "7.0.0",
"lodash": "4.17.15",
"lodash": "^4.17.15",
"meow": "5.0.0",
"regenerator-runtime": "0.13.3",
"resolve-from": "5.0.0",
"resolve-global": "1.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"@commitlint/execute-rule": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-9.0.1.tgz",
"integrity": "sha512-fxnLadXs59qOBE9dInfQjQ4DmbGToQ0NjfqqmN6N8qS+KsCecO6N0mMUrC95et9xTeimFRr+0l9UMfmRVHNS/w==",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"@commitlint/load": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-9.0.1.tgz",
"integrity": "sha512-6ix/pUjVAggmDLTcnpyk0bgY3H9UBBTsEeFvTkHV+WQ6LNIxsQk8SwEOEZzWHUqt0pxqMQeiUgYeSZsSw2+uiw==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"@commitlint/config-conventional": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-8.3.4.tgz",
"integrity": "sha512-w0Yc5+aVAjZgjYqx29igBOnVCj8O22gy3Vo6Fyp7PwoS7+AYS1x3sN7IBq6i7Ae15Mv5P+rEx1pkxXo5zOMe4g==",
"dev": true,
"requires": {
"conventional-changelog-conventionalcommits": "4.2.1"
}
},
"@commitlint/ensure": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-8.3.4.tgz",
"integrity": "sha512-8NW77VxviLhD16O3EUd02lApMFnrHexq10YS4F4NftNoErKbKaJ0YYedktk2boKrtNRf/gQHY/Qf65edPx4ipw==",
"dev": true,
"requires": {
"lodash": "4.17.15"
}
},
"@commitlint/execute-rule": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-8.3.4.tgz",
"integrity": "sha512-f4HigYjeIBn9f7OuNv5zh2y5vWaAhNFrfeul8CRJDy82l3Y+09lxOTGxfF3uMXKrZq4LmuK6qvvRCZ8mUrVvzQ==",
"dev": true
},
"@commitlint/format": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-8.3.4.tgz",
"integrity": "sha512-809wlQ/ND6CLZON+w2Rb3YM2TLNDfU2xyyqpZeqzf2reJNpySMSUAeaO/fNDJSOKIsOsR3bI01rGu6hv28k+Nw==",
"dev": true,
"requires": {
"chalk": "^2.0.1"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
"@commitlint/execute-rule": "^9.0.1",
"@commitlint/resolve-extends": "^9.0.1",
"@commitlint/types": "^9.0.1",
"chalk": "3.0.0",
"cosmiconfig": "^6.0.0",
"lodash": "^4.17.15",
"resolve-from": "^5.0.0"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"@commitlint/resolve-extends": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-9.0.1.tgz",
"integrity": "sha512-o6Lya2ILg1tEfWatS5x8w4ImvDzwb1whxsr2c/cxVCFqLF4hxHHHniZ0NJ+HFhYa1kBsYeKlD1qn9fHX5Y1+PQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"@commitlint/is-ignored": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-8.3.5.tgz",
"integrity": "sha512-Zo+8a6gJLFDTqyNRx53wQi/XTiz8mncvmWf/4oRG+6WRcBfjSSHY7KPVj5Y6UaLy2EgZ0WQ2Tt6RdTDeQiQplA==",
"dev": true,
"requires": {
"semver": "6.3.0"
}
},
"@commitlint/lint": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-8.3.5.tgz",
"integrity": "sha512-02AkI0a6PU6rzqUvuDkSi6rDQ2hUgkq9GpmdJqfai5bDbxx2939mK4ZO+7apbIh4H6Pae7EpYi7ffxuJgm+3hQ==",
"dev": true,
"requires": {
"@commitlint/is-ignored": "^8.3.5",
"@commitlint/parse": "^8.3.4",
"@commitlint/rules": "^8.3.4",
"babel-runtime": "^6.23.0",
"lodash": "4.17.15"
}
},
"@commitlint/load": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-8.3.5.tgz",
"integrity": "sha512-poF7R1CtQvIXRmVIe63FjSQmN9KDqjRtU5A6hxqXBga87yB2VUJzic85TV6PcQc+wStk52cjrMI+g0zFx+Zxrw==",
"dev": true,
"requires": {
"@commitlint/execute-rule": "^8.3.4",
"@commitlint/resolve-extends": "^8.3.5",
"babel-runtime": "^6.23.0",
"chalk": "2.4.2",
"cosmiconfig": "^5.2.0",
"lodash": "4.17.15",
"resolve-from": "^5.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
"import-fresh": "^3.0.0",
"lodash": "^4.17.15",
"resolve-from": "^5.0.0",
"resolve-global": "^1.0.0"
}
},
"resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"core-js": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
"dev": true
},
"cosmiconfig": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.7.2"
}
}
}
},
"@commitlint/message": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-8.3.4.tgz",
"integrity": "sha512-nEj5tknoOKXqBsaQtCtgPcsAaf5VCg3+fWhss4Vmtq40633xLq0irkdDdMEsYIx8rGR0XPBTukqzln9kAWCkcA==",
"dev": true
},
"@commitlint/parse": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-8.3.4.tgz",
"integrity": "sha512-b3uQvpUQWC20EBfKSfMRnyx5Wc4Cn778bVeVOFErF/cXQK725L1bYFvPnEjQO/GT8yGVzq2wtLaoEqjm1NJ/Bw==",
"dev": true,
"requires": {
"conventional-changelog-angular": "^1.3.3",
"conventional-commits-parser": "^3.0.0",
"lodash": "^4.17.11"
}
},
"@commitlint/read": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-8.3.4.tgz",
"integrity": "sha512-FKv1kHPrvcAG5j+OSbd41IWexsbLhfIXpxVC/YwQZO+FR0EHmygxQNYs66r+GnhD1EfYJYM4WQIqd5bJRx6OIw==",
"dev": true,
"requires": {
"@commitlint/top-level": "^8.3.4",
"@marionebl/sander": "^0.6.0",
"babel-runtime": "^6.23.0",
"git-raw-commits": "^2.0.0"
}
},
"@commitlint/resolve-extends": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-8.3.5.tgz",
"integrity": "sha512-nHhFAK29qiXNe6oH6uG5wqBnCR+BQnxlBW/q5fjtxIaQALgfoNLHwLS9exzbIRFqwJckpR6yMCfgMbmbAOtklQ==",
"dev": true,
"requires": {
"import-fresh": "^3.0.0",
"lodash": "4.17.15",
"resolve-from": "^5.0.0",
"resolve-global": "^1.0.0"
},
"dependencies": {
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
@ -278,41 +201,299 @@
}
}
},
"parse-json": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
"integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1",
"lines-and-columns": "^1.1.6"
}
},
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
"dev": true
},
"resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"@commitlint/config-conventional": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-9.0.1.tgz",
"integrity": "sha512-5rGu8aT4nRhWKrd5SpXqKJKLM07wXi4X5KVD9EEAuucAh2iZgfJJK9HKZNKGEKLKBQSWlnXE6UvkeEjJgi6TPQ==",
"dev": true,
"requires": {
"conventional-changelog-conventionalcommits": "4.2.3"
}
},
"@commitlint/ensure": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-9.0.1.tgz",
"integrity": "sha512-z8SEkfbn0lMnAtt7Hp3A8hE3CRCDsg+Eu3Xj1UJakOyCPJgHE1/vEyM2DO2dxTXVKuttiHeLDnUSHCxklm78Ng==",
"dev": true,
"requires": {
"@commitlint/types": "^9.0.1",
"lodash": "^4.17.15"
}
},
"@commitlint/execute-rule": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-8.3.4.tgz",
"integrity": "sha512-f4HigYjeIBn9f7OuNv5zh2y5vWaAhNFrfeul8CRJDy82l3Y+09lxOTGxfF3uMXKrZq4LmuK6qvvRCZ8mUrVvzQ==",
"dev": true,
"optional": true
},
"@commitlint/format": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-9.0.1.tgz",
"integrity": "sha512-5oY7Jyve7Bfnx0CdbxFcpRKq92vUANFq3MVbz/ZTgvuYgUeMuYsSEwW6MJtOgOhHBQ2vZP/uPdxwmU+6pWZHcg==",
"dev": true,
"requires": {
"chalk": "^3.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"@commitlint/is-ignored": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-9.0.1.tgz",
"integrity": "sha512-doGBfQgbsi48Hc48runGdN0TQFvf5XZizck8cylQdGG/3w+YwX9WkplEor7cvz8pmmuD6PpfpdukHSKlR8KmHQ==",
"dev": true,
"requires": {
"@commitlint/types": "^9.0.1",
"semver": "7.1.3"
}
},
"@commitlint/lint": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-9.0.1.tgz",
"integrity": "sha512-EAn4E6aGWZ96Dg9LN28kdELqkyFOAUGlXWmanMdWxGFGdOf24ZHzlVsbr/Yb1oSBUE2KVvAF5W2Mzn2+Ge5rOg==",
"dev": true,
"requires": {
"@commitlint/is-ignored": "^9.0.1",
"@commitlint/parse": "^9.0.1",
"@commitlint/rules": "^9.0.1",
"@commitlint/types": "^9.0.1"
}
},
"@commitlint/load": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-8.3.5.tgz",
"integrity": "sha512-poF7R1CtQvIXRmVIe63FjSQmN9KDqjRtU5A6hxqXBga87yB2VUJzic85TV6PcQc+wStk52cjrMI+g0zFx+Zxrw==",
"dev": true,
"optional": true,
"requires": {
"@commitlint/execute-rule": "^8.3.4",
"@commitlint/resolve-extends": "^8.3.5",
"babel-runtime": "^6.23.0",
"chalk": "2.4.2",
"cosmiconfig": "^5.2.0",
"lodash": "4.17.15",
"resolve-from": "^5.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true,
"optional": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"@commitlint/message": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-9.0.1.tgz",
"integrity": "sha512-9rKnOeBV5s5hnV895aE3aMgciC27kAjkV9BYVQOWRjZdXHFZxa+OZ94mkMp+Hcr61W++fox1JJpPiTuCTDX3TQ==",
"dev": true
},
"@commitlint/parse": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-9.0.1.tgz",
"integrity": "sha512-O39yMSMFdBtqwyM5Ld7RT6OGeI7jiXB9UUb09liIXIkltaZZo6CeoBD9hyfRWpaw81SiGL4OwHzp92mYVHLmow==",
"dev": true,
"requires": {
"conventional-changelog-angular": "^5.0.0",
"conventional-commits-parser": "^3.0.0"
}
},
"@commitlint/read": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-9.0.1.tgz",
"integrity": "sha512-EYbel85mAiHb56bS5jBJ71lEaGjTnkSJLxTV1u6dpxdSBkRdmAn2DSPd6KQSbwYGUlPCR+pAZeZItT1y0Xk3hg==",
"dev": true,
"requires": {
"@commitlint/top-level": "^9.0.1",
"fs-extra": "^8.1.0",
"git-raw-commits": "^2.0.0"
}
},
"@commitlint/resolve-extends": {
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-8.3.5.tgz",
"integrity": "sha512-nHhFAK29qiXNe6oH6uG5wqBnCR+BQnxlBW/q5fjtxIaQALgfoNLHwLS9exzbIRFqwJckpR6yMCfgMbmbAOtklQ==",
"dev": true,
"optional": true,
"requires": {
"import-fresh": "^3.0.0",
"lodash": "4.17.15",
"resolve-from": "^5.0.0",
"resolve-global": "^1.0.0"
},
"dependencies": {
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"dev": true,
"optional": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
},
"dependencies": {
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"optional": true
}
}
},
"resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true,
"optional": true
}
}
},
"@commitlint/rules": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-8.3.4.tgz",
"integrity": "sha512-xuC9dlqD5xgAoDFgnbs578cJySvwOSkMLQyZADb1xD5n7BNcUJfP8WjT9W1Aw8K3Wf8+Ym/ysr9FZHXInLeaRg==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-9.0.1.tgz",
"integrity": "sha512-K9IiQzF/C2tP/0mQUPSkOtmAEUleRQhZK1NFLVbsd6r4uobaczjPSYvEH+cuSHlD9b3Ori7PRiTgVBAZTH5ORQ==",
"dev": true,
"requires": {
"@commitlint/ensure": "^8.3.4",
"@commitlint/message": "^8.3.4",
"@commitlint/to-lines": "^8.3.4",
"babel-runtime": "^6.23.0"
"@commitlint/ensure": "^9.0.1",
"@commitlint/message": "^9.0.1",
"@commitlint/to-lines": "^9.0.1",
"@commitlint/types": "^9.0.1"
}
},
"@commitlint/to-lines": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-8.3.4.tgz",
"integrity": "sha512-5AvcdwRsMIVq0lrzXTwpbbG5fKRTWcHkhn/hCXJJ9pm1JidsnidS1y0RGkb3O50TEHGewhXwNoavxW9VToscUA==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-9.0.1.tgz",
"integrity": "sha512-FHiXPhFgGnvekF4rhyl1daHimEHkr81pxbHAmWG/0SOCehFr5THsWGoUYNNBMF7rdwUuVq4tXJpEOFiWBGKigg==",
"dev": true
},
"@commitlint/top-level": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-8.3.4.tgz",
"integrity": "sha512-nOaeLBbAqSZNpKgEtO6NAxmui1G8ZvLG+0wb4rvv6mWhPDzK1GNZkCd8FUZPahCoJ1iHDoatw7F8BbJLg4nDjg==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-9.0.1.tgz",
"integrity": "sha512-AjCah5y7wu9F/hOwMnqsujPRWlKerX79ZGf+UfBpOdAh+USdV7a/UfQaqjgCzkxy5GcNO9ER5A+2mWrUHxJ0hQ==",
"dev": true,
"requires": {
"find-up": "^4.0.0"
}
},
"@commitlint/types": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-9.0.1.tgz",
"integrity": "sha512-wo2rHprtDzTHf4tiSxavktJ52ntiwmg7eHNGFLH38G1of8OfGVwOc1sVbpM4jN/HK/rCMhYOi6xzoPqsv0537A==",
"dev": true
},
"@fullhuman/postcss-purgecss": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.2.0.tgz",
@ -373,17 +554,6 @@
}
}
},
"@marionebl/sander": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@marionebl/sander/-/sander-0.6.1.tgz",
"integrity": "sha1-GViWWHTyS8Ub5Ih1/rUNZC/EH3s=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.3",
"mkdirp": "^0.5.1",
"rimraf": "^2.5.2"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
@ -645,30 +815,12 @@
}
}
},
"babel-polyfill": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
"integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
"dev": true,
"requires": {
"babel-runtime": "^6.26.0",
"core-js": "^2.5.0",
"regenerator-runtime": "^0.10.5"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
"dev": true
}
}
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"optional": true,
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
@ -1248,9 +1400,9 @@
"dev": true
},
"conventional-changelog-angular": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz",
"integrity": "sha512-suQnFSqCxRwyBxY68pYTsFkG0taIdinHLNEAX5ivtw8bCRnIgnpvcHmlR/yjUyZIrNPYAoXlY1WiEKWgSE4BNg==",
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.10.tgz",
"integrity": "sha512-k7RPPRs0vp8+BtPsM9uDxRl6KcgqtCJmzRD1wRtgqmhQ96g8ifBGo9O/TZBG23jqlXS/rg8BKRDELxfnQQGiaA==",
"dev": true,
"requires": {
"compare-func": "^1.3.1",
@ -1258,13 +1410,13 @@
}
},
"conventional-changelog-conventionalcommits": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.2.1.tgz",
"integrity": "sha512-vC02KucnkNNap+foDKFm7BVUSDAXktXrUJqGszUuYnt6T0J2azsbYz/w9TDc3VsrW2v6JOtiQWVcgZnporHr4Q==",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.2.3.tgz",
"integrity": "sha512-atGa+R4vvEhb8N/8v3IoW59gCBJeeFiX6uIbPu876ENAmkMwsenyn0R21kdDHJFLQdy6zW4J6b4xN8KI3b9oww==",
"dev": true,
"requires": {
"compare-func": "^1.3.1",
"lodash": "^4.2.1",
"lodash": "^4.17.15",
"q": "^1.5.1"
}
},
@ -1322,7 +1474,8 @@
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -2621,9 +2774,9 @@
"dev": true
},
"lint-staged": {
"version": "10.2.9",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.9.tgz",
"integrity": "sha512-ziRAuXEqvJLSXg43ezBpHxRW8FOJCXISaXU//BWrxRrp5cBdRkIx7g5IsB3OI45xYGE0S6cOacfekSjDyDKF2g==",
"version": "10.2.11",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.11.tgz",
"integrity": "sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA==",
"dev": true,
"requires": {
"chalk": "^4.0.0",
@ -2754,9 +2907,9 @@
}
},
"listr2": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-2.1.3.tgz",
"integrity": "sha512-6oy3QhrZAlJGrG8oPcRp1hix1zUpb5AvyvZ5je979HCyf48tIj3Hn1TG5+rfyhz30t7HfySH/OIaVbwrI2kruA==",
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-2.1.8.tgz",
"integrity": "sha512-Op+hheiChfAphkJ5qUxZtHgyjlX9iNnAeFS/S134xw7mVSg0YVrQo1IY4/K+ElY6XgOPg2Ij4z07urUXR+YEew==",
"dev": true,
"requires": {
"chalk": "^4.0.0",
@ -3347,15 +3500,6 @@
}
}
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -4238,7 +4382,8 @@
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
"dev": true,
"optional": true
},
"regex-not": {
"version": "1.0.2",
@ -4336,15 +4481,6 @@
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"run-async": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@ -4388,9 +4524,9 @@
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz",
"integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==",
"dev": true
},
"semver-compare": {
@ -4875,11 +5011,12 @@
"dev": true
},
"through2": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
"integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"dev": true,
"requires": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
},

View File

@ -14,13 +14,13 @@
"commit": "git-cz"
},
"devDependencies": {
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@commitlint/cli": "^9.0.1",
"@commitlint/config-conventional": "^9.0.1",
"@prettier/plugin-php": "^0.14.2",
"@tailwindcss/custom-forms": "^0.2.1",
"cz-conventional-changelog": "^3.2.0",
"husky": "^4.2.5",
"lint-staged": "^10.2.9",
"lint-staged": "^10.2.11",
"postcss-cli": "^7.1.1",
"prettier": "2.0.5",
"tailwindcss": "^1.4.6"
@ -36,7 +36,7 @@
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
"path": "./node_modules/cz-conventional-changelog"
}
}
}