Commit 1202ba35 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat(podcast): create a podcast using form

parent 9daa0df0
......@@ -97,6 +97,7 @@ Populate the database with the required data:
```bash
# Populates all categories
docker-compose run --rm app php spark db:seed CategorySeeder
docker-compose run --rm app php spark db:seed LanguageSeeder
```
### Start hacking
......
<?php namespace App\Controllers;
use App\Models\CategoryModel;
use App\Models\LanguageModel;
use App\Models\PodcastModel;
use RuntimeException;
class Podcast extends BaseController
{
public function index()
{
return view('podcast/index.php');
}
public function create()
{
$model = new PodcastModel();
if (!$this->validate([
'title' => 'required',
'name' => 'required|alpha_dash',
'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',
'type' => 'required',
])) {
$langs = explode(',', $this->request->getServer('HTTP_ACCEPT_LANGUAGE'));
$browser_lang = '';
if (!empty($langs)) {
$browser_lang = substr($langs[0], 0, 2);
}
$languageModel = new LanguageModel();
$categoryModel = new CategoryModel();
$data = [
'languages' => $languageModel->findAll(),
'categories' => $categoryModel->findAll(),
'browser_lang' => $browser_lang,
];
echo view('podcast/create', $data);
} else {
$image = $this->request->getFile('image');
if (!$image->isValid()) {
throw new RuntimeException($image->getErrorString() . '(' . $image->getError() . ')');
}
$image_path = $image->store();
$model->save([
'title' => $this->request->getVar('title'),
'name' => $this->request->getVar('name'),
'description' => $this->request->getVar('description'),
'episode_description_footer' => $this->request->getVar('episode_description_footer'),
'image' => $image_path,
'language' => $this->request->getVar('language'),
'category' => $this->request->getVar('category'),
'explicit' => $this->request->getVar('explicit') or false,
'author' => $this->request->getVar('author'),
'owner_name' => $this->request->getVar('owner_name'),
'owner_email' => $this->request->getVar('owner_email'),
'type' => $this->request->getVar('type'),
'copyright' => $this->request->getVar('copyright'),
'block' => $this->request->getVar('block') or false,
'complete' => $this->request->getVar('complete') or false,
'custom_html_head' => $this->request->getVar('custom_html_head'),
]);
echo view('podcast/success');
}
}
}
......@@ -10,26 +10,30 @@ class AddCategories extends Migration
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => TRUE,
'id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
],
'parent_id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => TRUE
'parent_id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
],
'apple_category' => [
'type' => 'VARCHAR',
'constraint' => '1024',
'code' => [
'type' => 'VARCHAR',
'constraint' => 1024,
],
'apple_category' => [
'type' => 'VARCHAR',
'constraint' => 1024,
],
'google_category' => [
'type' => 'VARCHAR',
'constraint' => '1024',
'type' => 'VARCHAR',
'constraint' => 1024,
],
]);
$this->forge->addKey('id', TRUE);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('parent_id', 'categories', 'id');
$this->forge->createTable('categories');
}
......
<?php
namespace App\Database\Migrations;
use \CodeIgniter\Database\Migration;
class AddLanguages extends Migration
{
public function up()
{
$this->forge->addField([
'code' => [
'type' => 'VARCHAR',
'constraint' => 2,
],
]);
$this->forge->addKey('code', true);
$this->forge->createTable('languages');
}
public function down()
{
$this->forge->dropTable('languages');
}
}
<?php
namespace App\Database\Migrations;
use \CodeIgniter\Database\Migration;
class AddPodcasts extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'BIGINT',
'constraint' => 20,
'unsigned' => true,
'auto_increment' => true,
'comment' => 'The podcast ID',
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'comment' => 'The show title. It’s important to have a clear, concise name for your podcast. Make your title specific. A show titled Our Community Bulletin is too vague to attract many subscribers, no matter how compelling the content. Pay close attention to the title as Apple Podcasts uses this field for search. If you include a long list of keywords in an attempt to game podcast search, your show may be removed from the Apple directory.',
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 191,
'unique' => true,
'comment' => 'Unique podcast string identifier.',
],
'description' => [
'type' => 'TEXT',
'null' => true,
'comment' => 'The show description. Where description is text containing one or more sentences describing your podcast to potential listeners. The maximum amount of text allowed for this tag is 4000 characters. To include links in your description or rich HTML, adhere to the following technical guidelines: enclose all portions of your XML that contain embedded HTML in a CDATA section to prevent formatting issues, and to ensure proper link functionality.',
'null' => true,
],
'episode_description_footer' => [
'type' => 'TEXT',
'null' => true,
],
'image' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'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.',
],
'language' => [
'type' => 'VARCHAR',
'constraint' => 2,
'comment' => 'The language spoken on the show. Because Apple Podcasts is available in territories around the world, it is critical to specify the language of a podcast. Apple Podcasts only supports values from the ISO 639 list (two-letter language codes, with some possible modifiers, such as "en-us"). Invalid language codes will cause your feed to fail Apple validation.',
],
'category' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'comment' => 'The show category information. For a complete list of categories and subcategories, see Apple Podcasts categories. Select the category that best reflects the content of your show. If available, you can also define a subcategory. Although you can specify more than one category and subcategory in your RSS feed, Apple Podcasts only recognizes the first category and subcategory. When specifying categories and subcategories, be sure to properly escape ampersands.',
'null' => true,
],
'explicit' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
'comment' => 'The podcast parental advisory information. The explicit value can be one of the following: True: If you specify true, indicating the presence of explicit content, Apple Podcasts displays an Explicit parental advisory graphic for your podcast. Podcasts containing explicit material aren’t available in some Apple Podcasts territories. False: If you specify false, indicating that your podcast doesn’t contain explicit language or adult content, Apple Podcasts displays a Clean parental advisory graphic for your podcast.',
],
'author' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'comment' => 'The group responsible for creating the show. Show author most often refers to the parent company or network of a podcast, but it can also be used to identify the host(s) if none exists. Author information is especially useful if a company or organization publishes multiple podcasts. Providing this information will allow listeners to see all shows created by the same entity.',
'null' => true,
],
'owner_name' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'owner_name' => 'The podcast owner name. Note: The owner information is for administrative communication about the podcast and isn’t displayed in Apple Podcasts.',
'null' => true,
],
'owner_email' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'owner_email' => 'The podcast owner email address. Note: The owner information is for administrative communication about the podcast and isn’t displayed in Apple Podcasts. Please make sure the email address is active and monitored.',
'null' => true,
],
'type' => [
'type' => 'ENUM',
'constraint' => ['episodic', 'serial'],
'default' => 'episodic',
'comment' => 'The type of show. If your show is Serial you must use this tag. Its values can be one of the following: episodic (default). Specify episodic when episodes are intended to be consumed without any specific order. Apple Podcasts will present newest episodes first and display the publish date (required) of each episode. If organized into seasons, the newest season will be presented first - otherwise, episodes will be grouped by year published, newest first. For new subscribers, Apple Podcasts adds the newest, most recent episode in their Library. serial. Specify serial when episodes are intended to be consumed in sequential order. Apple Podcasts will present the oldest episodes first and display the episode numbers (required) of each episode. If organized into seasons, the newest season will be presented first and <itunes:episode> numbers must be given for each episode. For new subscribers, Apple Podcasts adds the first episode to their Library, or the entire current season if using seasons',
],
'copyright' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'comment' => 'The show copyright details. If your show is copyrighted you should use this tag.',
'null' => true,
],
'block' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
'comment' => 'The podcast show or hide status. If you want your show removed from the Apple directory, use this tag. Specifying the <itunes:block> tag with a Yes value, prevents the entire podcast from appearing in Apple Podcasts. Specifying any value other than Yes has no effect.',
],
'complete' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
'comment' => 'The podcast update status. If you will never publish another episode to your show, use this tag. Specifying the <itunes:complete> tag with a Yes value indicates that a podcast is complete and you will not post any more episodes in the future. Specifying any value other than Yes has no effect.',
],
'custom_html_head' => [
'type' => 'TEXT',
'null' => true,
],
'created_at' => [
'type' => 'TIMESTAMP',
],
'updated_at' => [
'type' => 'TIMESTAMP',
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('podcasts');
}
public function down()
{
$this->forge->dropTable('podcasts');
}
}
This diff is collapsed.
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class LanguageSeeder extends Seeder
{
public function run()
{
$data = array(
array('code' => 'ab'),
array('code' => 'aa'),
array('code' => 'af'),
array('code' => 'ak'),
array('code' => 'sq'),
array('code' => 'am'),
array('code' => 'ar'),
array('code' => 'an'),
array('code' => 'hy'),
array('code' => 'as'),
array('code' => 'av'),
array('code' => 'ae'),
array('code' => 'ay'),
array('code' => 'az'),
array('code' => 'bm'),
array('code' => 'ba'),
array('code' => 'eu'),
array('code' => 'be'),
array('code' => 'bn'),
array('code' => 'bh'),
array('code' => 'bi'),
array('code' => 'bs'),
array('code' => 'br'),
array('code' => 'bg'),
array('code' => 'my'),
array('code' => 'ca'),
array('code' => 'km'),
array('code' => 'ch'),
array('code' => 'ce'),
array('code' => 'ny'),
array('code' => 'zh'),
array('code' => 'cu'),
array('code' => 'cv'),
array('code' => 'kw'),
array('code' => 'co'),
array('code' => 'cr'),
array('code' => 'hr'),
array('code' => 'cs'),
array('code' => 'da'),
array('code' => 'dv'),
array('code' => 'nl'),
array('code' => 'dz'),
array('code' => 'en'),
array('code' => 'eo'),
array('code' => 'et'),
array('code' => 'ee'),
array('code' => 'fo'),
array('code' => 'fj'),
array('code' => 'fi'),
array('code' => 'fr'),
array('code' => 'ff'),
array('code' => 'gd'),
array('code' => 'gl'),
array('code' => 'lg'),
array('code' => 'ka'),
array('code' => 'de'),
array('code' => 'ki'),
array('code' => 'el'),
array('code' => 'kl'),
array('code' => 'gn'),
array('code' => 'gu'),
array('code' => 'ht'),
array('code' => 'ha'),
array('code' => 'he'),
array('code' => 'hz'),
array('code' => 'hi'),
array('code' => 'ho'),
array('code' => 'hu'),
array('code' => 'is'),
array('code' => 'io'),
array('code' => 'ig'),
array('code' => 'id'),
array('code' => 'ia'),
array('code' => 'ie'),
array('code' => 'iu'),
array('code' => 'ik'),
array('code' => 'ga'),
array('code' => 'it'),
array('code' => 'ja'),
array('code' => 'jv'),
array('code' => 'kn'),
array('code' => 'kr'),
array('code' => 'ks'),
array('code' => 'kk'),
array('code' => 'rw'),
array('code' => 'kv'),
array('code' => 'kg'),
array('code' => 'ko'),
array('code' => 'kj'),
array('code' => 'ku'),
array('code' => 'ky'),
array('code' => 'lo'),
array('code' => 'la'),
array('code' => 'lv'),
array('code' => 'lb'),
array('code' => 'li'),
array('code' => 'ln'),
array('code' => 'lt'),
array('code' => 'lu'),
array('code' => 'mk'),
array('code' => 'mg'),
array('code' => 'ms'),
array('code' => 'ml'),
array('code' => 'mt'),
array('code' => 'gv'),
array('code' => 'mi'),
array('code' => 'mr'),
array('code' => 'mh'),
array('code' => 'ro'),
array('code' => 'mn'),
array('code' => 'na'),
array('code' => 'nv'),
array('code' => 'nd'),
array('code' => 'ng'),
array('code' => 'ne'),
array('code' => 'se'),
array('code' => 'no'),
array('code' => 'nb'),
array('code' => 'nn'),
array('code' => 'ii'),
array('code' => 'oc'),
array('code' => 'oj'),
array('code' => 'or'),
array('code' => 'om'),
array('code' => 'os'),
array('code' => 'pi'),
array('code' => 'pa'),
array('code' => 'ps'),
array('code' => 'fa'),
array('code' => 'pl'),
array('code' => 'pt'),
array('code' => 'qu'),
array('code' => 'rm'),
array('code' => 'rn'),
array('code' => 'ru'),
array('code' => 'sm'),
array('code' => 'sg'),
array('code' => 'sa'),
array('code' => 'sc'),
array('code' => 'sr'),
array('code' => 'sn'),
array('code' => 'sd'),
array('code' => 'si'),
array('code' => 'sk'),
array('code' => 'sl'),
array('code' => 'so'),
array('code' => 'st'),
array('code' => 'nr'),
array('code' => 'es'),
array('code' => 'su'),
array('code' => 'sw'),
array('code' => 'ss'),
array('code' => 'sv'),
array('code' => 'tl'),
array('code' => 'ty'),
array('code' => 'tg'),
array('code' => 'ta'),
array('code' => 'tt'),
array('code' => 'te'),
array('code' => 'th'),
array('code' => 'bo'),
array('code' => 'ti'),
array('code' => 'to'),
array('code' => 'ts'),
array('code' => 'tn'),
array('code' => 'tr'),
array('code' => 'tk'),
array('code' => 'tw'),
array('code' => 'ug'),
array('code' => 'uk'),
array('code' => 'ur'),
array('code' => 'uz'),
array('code' => 've'),
array('code' => 'vi'),
array('code' => 'vo'),
array('code' => 'wa'),
array('code' => 'cy'),
array('code' => 'fy'),
array('code' => 'wo'),
array('code' => 'xh'),
array('code' => 'yi'),
array('code' => 'yo'),
array('code' => 'za'),
array('code' => 'zu'),
);
$this->db->table('languages')->insertBatch($data);
}
}
<?php
namespace App\Entities;
use CodeIgniter\Entity;
class Language extends Entity
{
protected $casts = [
'code' => 'string',
];
}
<?php
namespace App\Entities;
use CodeIgniter\Entity;
class Podcast extends Entity
{
protected $casts = [
'title' => 'string',
'name' => 'string',
'description' => 'string',
'episode_description_footer' => '?string',
'image' => 'string',
'language' => 'string',
'category' => 'array',
'explicit' => 'boolean',
'author' => '?string',
'owner_name' => '?string',
'owner_email' => '?string',
'type' => '?string',
'copyright' => '?string',
'block' => 'boolean',
'complete' => 'boolean',
'custom_html_head' => '?string',
];
}
<?
return [
"create" => "Create a Podcast",
"form" => [
'title' => 'Title',
'name' => 'Name',
'description' => 'Description',
'episode_description_footer' => 'Episode description footer',
'image' => 'Image',
'language' => 'Language',
'category' => 'Category',
'explicit' => 'Explicit',
'author' => 'Author',
'owner_name' => 'Owner name',
'owner_email' => 'Owner email',
'type' => [
'label' => 'Type',
'episodic' => 'Episodic',
'serial' => 'Serial',
],
'copyright' => 'Copyright',
'block' => 'Block',
'complete' => 'Complete',
'custom_html_head' => 'Custom HTML code in <head/>',
'submit' => 'Create podcast',
],
"language_options" => [
'ab' => 'Abkhazian',
'aa' => 'Afar',
'af' => 'Afrikaans',
'ak' => 'Akan',
'sq' => 'Albanian',
'am' => 'Amharic',
'ar' => 'Arabic',
'an' => 'Aragonese',
'hy' => 'Armenian',
'as' => 'Assamese',
'av' => 'Avaric',
'ae' => 'Avestan',
'ay' => 'Aymara',
'az' => 'Azerbaijani',
'bm' => 'Bambara',
'ba' => 'Bashkir',
'eu' => 'Basque',
'be' => 'Belarusian',
'bn' => 'Bengali',
'bh' => 'Bihari languages',
'bi' => 'Bislama',
'bs' => 'Bosnian',
'br' => 'Breton',
'bg' => 'Bulgarian',
'my' => 'Burmese',
'ca' => 'Catalan, Valencian',
'km' => 'Central Khmer',
'ch' => 'Chamorro',
'ce' => 'Chechen',
'ny' => 'Chichewa, Chewa, Nyanja',
'zh' => 'Chinese',
'cu' => 'Church Slavonic, Old Bulgarian, Old Church Slavonic',
'cv' => 'Chuvash',
'kw' => 'Cornish',
'co' => 'Corsican',
'cr' => 'Cree',