From 99fbc8727d7e5153905fcadd6173c102cff50e1b Mon Sep 17 00:00:00 2001 From: Andrew Gardener Date: Wed, 13 Dec 2023 15:55:35 -0800 Subject: [PATCH] User testing changes - Reworked the entire export status interface - Use `AMP` instead of `Amplify` for brand - Switch contributor role to simple select2 (fixed list) - select2entity now start displaying immediately (no 2 char min) - form collections now have more detailed add button labels - moved episode transcript form text area to bottom of form to appear with transcript pdf upload - added labels to differentiate them - Added context to actions buttons (ex: `Edit Podcast` instead of `Edit`) - moved table captions to the top of the table - fixed select2 (+ select2entity) required fields not displaying form required validation on submit correctly - Added save & Continue to podcast/season/episode forms - Added image alt text and tooltip using image description - simplify Institutions into text input with datalist of Canadian Universities - New publishers and contributor->people are now added in modals (not automatically selected in form though) - fixed required label showing for collections (collection needs required so that subfields follow required settings but the * looks weird when doing this) - Allow float values for episode numbers - person/publishers are now locked to a podcast - added season/episode heading on their show pages so it is clearer where the user is - fixed select not focusing properly when opened --- composer.json | 2 +- composer.lock | 8 +- config/packages/tetranzselect2entity.yaml | 1 + config/packages/twig.yaml | 1 - migrations/2023/12/Version20231213005011.php | 33 + migrations/2023/12/Version20231215210331.php | 40 ++ migrations/2023/12/Version20231218213700.php | 49 ++ migrations/2023/12/Version20231218213930.php | 39 ++ public/css/base.css | 46 +- public/js/form.js | 147 +++-- src/Command/ImportPodcastCommand.php | 11 +- src/Config/ContributorRole.php | 599 ++++++++++++++++++ src/Controller/ContributorRoleController.php | 114 ---- src/Controller/DefaultController.php | 3 +- src/Controller/EpisodeController.php | 10 + src/Controller/InstitutionController.php | 114 ---- src/Controller/PersonController.php | 58 +- src/Controller/PodcastController.php | 45 +- src/Controller/PublisherController.php | 58 +- src/Controller/SeasonController.php | 12 +- src/Controller/ShareController.php | 2 +- src/DataFixtures/ContributionFixtures.php | 12 +- src/DataFixtures/ContributorRoleFixtures.php | 32 - src/DataFixtures/EpisodeFixtures.php | 2 +- src/DataFixtures/ExportFixtures.php | 3 +- src/DataFixtures/InstitutionFixtures.php | 32 - src/DataFixtures/PersonFixtures.php | 49 -- src/DataFixtures/PodcastFixtures.php | 38 +- src/DataFixtures/PublisherFixtures.php | 34 - src/DataFixtures/SeasonFixtures.php | 7 +- src/Entity/Contribution.php | 47 +- src/Entity/ContributorRole.php | 66 -- src/Entity/Episode.php | 29 +- src/Entity/Institution.php | 86 --- src/Entity/Person.php | 47 +- src/Entity/Podcast.php | 89 ++- src/Entity/Publisher.php | 14 + src/Entity/Season.php | 18 +- src/Form/ContributionType.php | 31 +- src/Form/ContributorRoleType.php | 41 -- src/Form/EpisodeType.php | 32 +- src/Form/InstitutionType.php | 42 -- src/Form/PersonType.php | 241 ++++++- src/Form/PodcastType.php | 17 +- src/Form/SeasonType.php | 14 +- src/Menu/Builder.php | 53 +- src/Message/ExportCleanupMessage.php | 3 +- src/Message/ExportMessage.php | 3 +- src/Message/ImportMessage.php | 3 +- src/MessageHandler/ExportCleanupHandler.php | 3 +- src/MessageHandler/ExportHandler.php | 3 +- src/MessageHandler/ImportHandler.php | 3 +- src/Repository/ContributorRoleRepository.php | 21 - src/Repository/InstitutionRepository.php | 54 -- src/Repository/PersonRepository.php | 13 +- src/Repository/PublisherRepository.php | 13 +- src/Security/PodcastVoter.php | 3 +- src/Service/BepressExport.php | 25 +- src/Service/ExportService.php | 5 +- src/Service/IslandoraExport.php | 4 +- templates/base.html.twig | 6 +- templates/contributor_role/edit.html.twig | 19 - templates/contributor_role/index.html.twig | 23 - templates/contributor_role/new.html.twig | 18 - .../contributor_role/partial/form.html.twig | 16 - templates/contributor_role/show.html.twig | 47 -- templates/default/privacy.html.twig | 2 +- templates/episode/edit.html.twig | 1 - templates/episode/new.html.twig | 1 - templates/episode/partial/form.html.twig | 9 +- templates/episode/partial/table.html.twig | 20 +- templates/episode/show.html.twig | 36 +- .../format/mods/partial/contribution.xml.twig | 6 +- .../mods/partial/identifier_season.xml.twig | 2 +- templates/export/index.html.twig | 7 +- templates/export/new.html.twig | 10 +- templates/export/show.html.twig | 1 - templates/import/new.html.twig | 10 +- templates/import/show.html.twig | 1 - templates/institution/edit.html.twig | 19 - templates/institution/index.html.twig | 56 -- templates/institution/new.html.twig | 18 - templates/institution/partial/form.html.twig | 16 - templates/institution/show.html.twig | 66 -- templates/person/edit.html.twig | 16 +- templates/person/index.html.twig | 41 +- templates/person/new.html.twig | 18 - templates/person/new_modal_content.html.twig | 15 + templates/person/partial/form.html.twig | 16 - templates/person/show.html.twig | 91 --- templates/podcast/edit.html.twig | 2 +- templates/podcast/index.html.twig | 24 +- templates/podcast/new.html.twig | 1 - templates/podcast/partial/form.html.twig | 9 +- templates/podcast/show.html.twig | 56 +- templates/publisher/edit.html.twig | 16 +- templates/publisher/index.html.twig | 35 +- templates/publisher/new.html.twig | 18 - .../publisher/new_modal_content.html.twig | 15 + templates/publisher/partial/form.html.twig | 16 - templates/publisher/show.html.twig | 94 --- templates/season/edit.html.twig | 1 - templates/season/new.html.twig | 1 - templates/season/partial/form.html.twig | 9 +- templates/season/show.html.twig | 26 +- templates/share/index.html.twig | 9 +- templates/status/partial/badge.html.twig | 39 -- templates/status/partial/episode.html.twig | 13 + templates/status/partial/heading.html.twig | 11 + templates/status/partial/podcast.html.twig | 19 + templates/status/partial/report.html.twig | 50 ++ templates/status/partial/season.html.twig | 18 + .../status/partial/status_items.html.twig | 10 + tests/Command/ImportPodcastCommandTest.php | 10 +- tests/Controller/ContributorRoleTest.php | 149 ----- tests/Controller/EpisodeTest.php | 8 +- tests/Controller/ExportTest.php | 10 +- tests/Controller/ImportTest.php | 14 +- tests/Controller/InstitutionTest.php | 147 ----- tests/Controller/PersonTest.php | 112 ++-- tests/Controller/PodcastTest.php | 59 +- tests/Controller/PublisherTest.php | 104 +-- tests/Controller/SeasonTest.php | 12 +- tests/Controller/ShareTest.php | 6 +- .../ContributorRoleRepositoryTest.php | 38 -- .../Repository/InstitutionRepositoryTest.php | 38 -- tests/Repository/PersonRepositoryTest.php | 17 +- tests/Repository/PublisherRepositoryTest.php | 16 +- 128 files changed, 2062 insertions(+), 2401 deletions(-) create mode 100644 migrations/2023/12/Version20231213005011.php create mode 100644 migrations/2023/12/Version20231215210331.php create mode 100644 migrations/2023/12/Version20231218213700.php create mode 100644 migrations/2023/12/Version20231218213930.php create mode 100644 src/Config/ContributorRole.php delete mode 100644 src/Controller/ContributorRoleController.php delete mode 100644 src/Controller/InstitutionController.php delete mode 100644 src/DataFixtures/ContributorRoleFixtures.php delete mode 100644 src/DataFixtures/InstitutionFixtures.php delete mode 100644 src/DataFixtures/PersonFixtures.php delete mode 100644 src/DataFixtures/PublisherFixtures.php delete mode 100644 src/Entity/ContributorRole.php delete mode 100644 src/Entity/Institution.php delete mode 100644 src/Form/ContributorRoleType.php delete mode 100644 src/Form/InstitutionType.php delete mode 100644 src/Repository/ContributorRoleRepository.php delete mode 100644 src/Repository/InstitutionRepository.php delete mode 100644 templates/contributor_role/edit.html.twig delete mode 100644 templates/contributor_role/index.html.twig delete mode 100644 templates/contributor_role/new.html.twig delete mode 100644 templates/contributor_role/partial/form.html.twig delete mode 100644 templates/contributor_role/show.html.twig delete mode 100644 templates/institution/edit.html.twig delete mode 100644 templates/institution/index.html.twig delete mode 100644 templates/institution/new.html.twig delete mode 100644 templates/institution/partial/form.html.twig delete mode 100644 templates/institution/show.html.twig delete mode 100644 templates/person/new.html.twig create mode 100644 templates/person/new_modal_content.html.twig delete mode 100644 templates/person/partial/form.html.twig delete mode 100644 templates/person/show.html.twig delete mode 100644 templates/publisher/new.html.twig create mode 100644 templates/publisher/new_modal_content.html.twig delete mode 100644 templates/publisher/partial/form.html.twig delete mode 100644 templates/publisher/show.html.twig delete mode 100644 templates/status/partial/badge.html.twig create mode 100644 templates/status/partial/episode.html.twig create mode 100644 templates/status/partial/heading.html.twig create mode 100644 templates/status/partial/podcast.html.twig create mode 100644 templates/status/partial/report.html.twig create mode 100644 templates/status/partial/season.html.twig create mode 100644 templates/status/partial/status_items.html.twig delete mode 100644 tests/Controller/ContributorRoleTest.php delete mode 100644 tests/Controller/InstitutionTest.php delete mode 100644 tests/Repository/ContributorRoleRepositoryTest.php delete mode 100644 tests/Repository/InstitutionRepositoryTest.php diff --git a/composer.json b/composer.json index 65cf0bb..5045e97 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "phpdocumentor/reflection-docblock": "^5.3", "scienta/doctrine-json-functions": "^5.3", "sensio/framework-extra-bundle": "^6.2", - "sfu-dhil/nines": "6.3.11", + "sfu-dhil/nines": "6.3.13", "simplepie/simplepie": "^1.8", "symfony/asset": "6.3.*", "symfony/console": "6.3.*", diff --git a/composer.lock b/composer.lock index 6a10a0c..ea090d9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4babda9a6168c0858866285cdb923262", + "content-hash": "81536aaf9ce599032b88438d403febaa", "packages": [ { "name": "beberlei/doctrineextensions", @@ -3890,11 +3890,11 @@ }, { "name": "sfu-dhil/nines", - "version": "v6.3.11", + "version": "v6.3.13", "source": { "type": "git", "url": "https://github.com/sfu-dhil/nines-bundles.git", - "reference": "a171e8f08309ad1371d8f0557d8eada37a15e7b3" + "reference": "fe294a3d31e777cb9d40f5fcd1aaff0306bfdc86" }, "require": { "doctrine/cache": "^2.2", @@ -3963,7 +3963,7 @@ } ], "description": "Some useful bundles.", - "time": "2023-10-23T23:35:24+00:00" + "time": "2023-12-16T00:10:39+00:00" }, { "name": "simplepie/simplepie", diff --git a/config/packages/tetranzselect2entity.yaml b/config/packages/tetranzselect2entity.yaml index 9d7d80d..ffcad86 100644 --- a/config/packages/tetranzselect2entity.yaml +++ b/config/packages/tetranzselect2entity.yaml @@ -1,3 +1,4 @@ tetranz_select2_entity: + minimum_input_length: 0 theme: 'bootstrap-5' width: 100% \ No newline at end of file diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index a30c22b..5a243de 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -18,7 +18,6 @@ twig: matomo_url: '%dhil.matomo_url%' matomo_siteid: '%dhil.matomo_siteid%' matomo_domain: '%dhil.matomo_domain%' - linker: '@Nines\UtilBundle\Services\EntityLinker' git_repo: '%env(default::string:GIT_REPO)%' git_commit: '%env(default::string:GIT_COMMIT)%' git_commit_short: '%env(default::string:GIT_COMMIT_SHORT)%' diff --git a/migrations/2023/12/Version20231213005011.php b/migrations/2023/12/Version20231213005011.php new file mode 100644 index 0000000..13ac4fb --- /dev/null +++ b/migrations/2023/12/Version20231213005011.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E1513CCE206'); + $this->addSql('DROP TABLE contributor_role'); + $this->addSql('DROP INDEX IDX_EA351E1513CCE206 ON contribution'); + $this->addSql('ALTER TABLE contribution ADD role VARCHAR(255) DEFAULT NULL, DROP contributor_role_id'); + } + + public function down(Schema $schema) : void { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE contributor_role (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(191) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, label VARCHAR(200) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, description LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', relator_term VARCHAR(10) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, FULLTEXT INDEX IDX_8421DF24EA750E8 (label), FULLTEXT INDEX IDX_8421DF246DE44026 (description), FULLTEXT INDEX IDX_8421DF24EA750E86DE44026 (label, description), UNIQUE INDEX UNIQ_8421DF245E237E06 (name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('ALTER TABLE contribution ADD contributor_role_id INT NOT NULL, DROP role'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E1513CCE206 FOREIGN KEY (contributor_role_id) REFERENCES contributor_role (id) ON DELETE CASCADE'); + $this->addSql('CREATE INDEX IDX_EA351E1513CCE206 ON contribution (contributor_role_id)'); + } +} diff --git a/migrations/2023/12/Version20231215210331.php b/migrations/2023/12/Version20231215210331.php new file mode 100644 index 0000000..e593ff9 --- /dev/null +++ b/migrations/2023/12/Version20231215210331.php @@ -0,0 +1,40 @@ +addSql('TRUNCATE TABLE contribution'); + $this->addSql('ALTER TABLE person DROP FOREIGN KEY FK_34DCD17610405986'); + $this->addSql('DROP TABLE institution'); + $this->addSql('ALTER TABLE episode CHANGE keywords keywords JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', CHANGE status status JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('DROP INDEX IDX_34DCD17610405986 ON person'); + $this->addSql('ALTER TABLE person ADD institution VARCHAR(255) DEFAULT NULL, DROP institution_id'); + $this->addSql('ALTER TABLE podcast CHANGE categories categories JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', CHANGE keywords keywords JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', CHANGE status status JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE season CHANGE status status JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\''); + } + + public function down(Schema $schema) : void { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE institution (id INT AUTO_INCREMENT NOT NULL, country VARCHAR(40) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, name VARCHAR(100) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', FULLTEXT INDEX institution_ft (name), UNIQUE INDEX institutions_uniq (country, name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('ALTER TABLE episode CHANGE keywords keywords JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', CHANGE status status JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE person ADD institution_id INT DEFAULT NULL, DROP institution'); + $this->addSql('ALTER TABLE person ADD CONSTRAINT FK_34DCD17610405986 FOREIGN KEY (institution_id) REFERENCES institution (id)'); + $this->addSql('CREATE INDEX IDX_34DCD17610405986 ON person (institution_id)'); + $this->addSql('ALTER TABLE season CHANGE status status JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE podcast CHANGE categories categories JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', CHANGE keywords keywords JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', CHANGE status status JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\''); + } +} diff --git a/migrations/2023/12/Version20231218213700.php b/migrations/2023/12/Version20231218213700.php new file mode 100644 index 0000000..d40f1c2 --- /dev/null +++ b/migrations/2023/12/Version20231218213700.php @@ -0,0 +1,49 @@ +addSql('ALTER TABLE contribution ADD roles JSON DEFAULT \'[]\' NOT NULL COMMENT \'(DC2Type:json)\', DROP role'); + $this->addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E15786136AB'); + $this->addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E15362B62A0'); + $this->addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E154EC001D1'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E15786136AB FOREIGN KEY (podcast_id) REFERENCES podcast (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E15362B62A0 FOREIGN KEY (episode_id) REFERENCES episode (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E154EC001D1 FOREIGN KEY (season_id) REFERENCES season (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE episode CHANGE number number DOUBLE PRECISION NOT NULL'); + $this->addSql('ALTER TABLE podcast DROP FOREIGN KEY FK_D7E805BD40C86FCE'); + $this->addSql('ALTER TABLE podcast ADD CONSTRAINT FK_D7E805BD40C86FCE FOREIGN KEY (publisher_id) REFERENCES publisher (id) ON DELETE SET NULL'); + $this->addSql('ALTER TABLE season DROP FOREIGN KEY FK_F0E45BA940C86FCE'); + $this->addSql('ALTER TABLE season ADD CONSTRAINT FK_F0E45BA940C86FCE FOREIGN KEY (publisher_id) REFERENCES publisher (id) ON DELETE SET NULL'); + } + + public function down(Schema $schema) : void { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE contribution ADD role VARCHAR(255) DEFAULT NULL, DROP roles'); + $this->addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E15786136AB'); + $this->addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E154EC001D1'); + $this->addSql('ALTER TABLE contribution DROP FOREIGN KEY FK_EA351E15362B62A0'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E15786136AB FOREIGN KEY (podcast_id) REFERENCES podcast (id)'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E154EC001D1 FOREIGN KEY (season_id) REFERENCES season (id)'); + $this->addSql('ALTER TABLE contribution ADD CONSTRAINT FK_EA351E15362B62A0 FOREIGN KEY (episode_id) REFERENCES episode (id)'); + $this->addSql('ALTER TABLE episode CHANGE number number INT NOT NULL'); + $this->addSql('ALTER TABLE season DROP FOREIGN KEY FK_F0E45BA940C86FCE'); + $this->addSql('ALTER TABLE season ADD CONSTRAINT FK_F0E45BA940C86FCE FOREIGN KEY (publisher_id) REFERENCES publisher (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE podcast DROP FOREIGN KEY FK_D7E805BD40C86FCE'); + $this->addSql('ALTER TABLE podcast ADD CONSTRAINT FK_D7E805BD40C86FCE FOREIGN KEY (publisher_id) REFERENCES publisher (id)'); + } +} diff --git a/migrations/2023/12/Version20231218213930.php b/migrations/2023/12/Version20231218213930.php new file mode 100644 index 0000000..d5402c6 --- /dev/null +++ b/migrations/2023/12/Version20231218213930.php @@ -0,0 +1,39 @@ +addSql('DELETE FROM person'); + $this->addSql('DELETE FROM publisher'); + $this->addSql('ALTER TABLE person ADD podcast_id INT NOT NULL'); + $this->addSql('ALTER TABLE person ADD CONSTRAINT FK_34DCD176786136AB FOREIGN KEY (podcast_id) REFERENCES podcast (id) ON DELETE CASCADE'); + $this->addSql('CREATE INDEX IDX_34DCD176786136AB ON person (podcast_id)'); + $this->addSql('ALTER TABLE publisher ADD podcast_id INT NOT NULL'); + $this->addSql('ALTER TABLE publisher ADD CONSTRAINT FK_9CE8D546786136AB FOREIGN KEY (podcast_id) REFERENCES podcast (id) ON DELETE CASCADE'); + $this->addSql('CREATE INDEX IDX_9CE8D546786136AB ON publisher (podcast_id)'); + } + + public function down(Schema $schema) : void { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE publisher DROP FOREIGN KEY FK_9CE8D546786136AB'); + $this->addSql('DROP INDEX IDX_9CE8D546786136AB ON publisher'); + $this->addSql('ALTER TABLE publisher DROP podcast_id'); + $this->addSql('ALTER TABLE person DROP FOREIGN KEY FK_34DCD176786136AB'); + $this->addSql('DROP INDEX IDX_34DCD176786136AB ON person'); + $this->addSql('ALTER TABLE person DROP podcast_id'); + } +} diff --git a/public/css/base.css b/public/css/base.css index 696f87b..77ff85f 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -195,6 +195,21 @@ div.collection-complex > div:not(:last-child) { color: #fff !important; background-color: RGBA(2,125,130,var(--bs-bg-opacity,1)) !important; } +.bg-primary { + --bs-primary-rgb: 2,125,130; +} +.text-primary { + --bs-primary-rgb: 2,125,130; +} +.link-primary { + color: #027D82 !important; +} +.alert-success { + --bs-alert-color: #027D82; + --bs-alert-bg: rgba(2,125,130 , 0.125); + --bs-alert-border-color: rgba(2,125,130 , 0.25); + } + /* amplify pink #DD1B70 or 221,27,122 */ .btn-danger { @@ -217,10 +232,13 @@ div.collection-complex > div:not(:last-child) { color: #fff !important; background-color: RGBA(221,27,122,var(--bs-bg-opacity,1)) !important; } +.bg-danger { + --bs-primary-rgb: 221,27,122; +} .alert-danger { - --bs-alert-color: #842029; - --bs-alert-bg: #f8d7da; - --bs-alert-border-color: #f5c2c7; + --bs-alert-color: #DD1B70; + --bs-alert-bg: rgba(0, 0, 0, 0.125); + --bs-alert-border-color: rgba(0, 0, 0, 0.25); } .btn-warning { --bs-btn-color: #fff; @@ -237,4 +255,26 @@ div.collection-complex > div:not(:last-child) { --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #DD1B70; --bs-btn-disabled-border-color: #DD1B70; +} + +select.select2-hidden-accessible { + position: relative !important; + display: block !important; + width: 0px !important; + height: 0px !important; + border: none !important; + top: calc(1.5em + .75rem + 2px) !important; +} + +textarea.tinymce { + position: relative !important; + display: block !important; + width: 0px !important; + height: 0px !important; + min-height: 0px !important; + padding: 0px !important; + border: none !important; + resize: none !important; + outline: none !important; + box-shadow: none !important; } \ No newline at end of file diff --git a/public/js/form.js b/public/js/form.js index 0d892d4..8b8f8fd 100644 --- a/public/js/form.js +++ b/public/js/form.js @@ -42,42 +42,33 @@ }); } - function simpleCollection() { - if ( $('.collection-simple').length == 0 ) { - return - } - $('.collection-simple').collection({ - init_with_n_elements: 1, - allow_up: false, - allow_down: false, - max: 400, - add_at_the_end: true, - add: '', - remove: '', - }); - } - function complexCollection() { if ( $('.collection-complex').length == 0 ) { return } - $('.collection-complex').collection({ - init_with_n_elements: 1, - allow_up: false, - allow_down: false, - max: 400, - add_at_the_end: true, - add: '', - remove: '', - after_add: function(collection, element){ - $(element).find('.select2entity').select2entity(); - if (tinymce && $(element).find('.tinymce').length > 0 ) { - $(element).find('.tinymce').each(function (index, textarea) { - tinymce.execCommand("mceAddEditor", false, textarea.id); + $('.collection-complex').each( (index, collectionEl) => { + const label = $(collectionEl).data('collection-label'); + $(collectionEl).collection({ + init_with_n_elements: 0, + allow_up: false, + allow_down: false, + max: 400, + add_at_the_end: true, + add: ` Add New ${label}`, + remove: '', + after_add: function(collection, element){ + $(element).find('.select2entity').select2entity(); + $(element).find('.select2-simple').select2({ + width: '100%', }); - } - return true; - }, + if (tinymce && $(element).find('.tinymce').length > 0 ) { + $(element).find('.tinymce').each(function (index, textarea) { + tinymce.execCommand("mceAddEditor", false, textarea.id); + }); + } + return true; + }, + }) }); } @@ -92,10 +83,13 @@ max: 400, position_field_selector: '.position-id', add_at_the_end: true, - add: ' Image', + add: ' Add New Image', remove: '', after_add: function(collection, element){ $(element).find('.select2entity').select2entity(); + $(element).find('.select2-simple').select2({ + width: '100%', + }); if (tinymce && $(element).find('.tinymce').length > 0 ) { $(element).find('.tinymce').each(function (index, textarea) { tinymce.execCommand("mceAddEditor", false, textarea.id); @@ -111,10 +105,13 @@ max: 400, position_field_selector: '.position-id', add_at_the_end: true, - add: ' Audio', + add: ' Add New Audio', remove: '', after_add: function(collection, element){ $(element).find('.select2entity').select2entity(); + $(element).find('.select2-simple').select2({ + width: '100%', + }); if (tinymce && $(element).find('.tinymce').length > 0 ) { $(element).find('.tinymce').each(function (index, textarea) { tinymce.execCommand("mceAddEditor", false, textarea.id); @@ -130,10 +127,13 @@ max: 400, position_field_selector: '.position-id', add_at_the_end: true, - add: ' Transcript', + add: ' Add New Transcript', remove: '', after_add: function(collection, element){ $(element).find('.select2entity').select2entity(); + $(element).find('.select2-simple').select2({ + width: '100%', + }); if (tinymce && $(element).find('.tinymce').length > 0 ) { $(element).find('.tinymce').each(function (index, textarea) { tinymce.execCommand("mceAddEditor", false, textarea.id); @@ -144,19 +144,6 @@ }); } - - function imageModals() { - $('#imgModal').on('show.bs.modal', function (event) { - var $button = $(event.relatedTarget); - // Button that triggered the modal - var $modal = $(this); - - $modal.find('#modalTitle').text($button.data('title')); - $modal.find('figcaption').html($button.parent().parent().find('.caption').clone()); - $modal.find("#modalImage").attr('src', $button.data('img')); - }); - } - function menuTabs() { const localStorageId = `tab-${location.href}-target` const lastShownTab = localStorage.getItem(localStorageId) @@ -188,13 +175,61 @@ alertList.forEach(function (alert) { new bootstrap.Alert(alert); }); // add alert dismissal + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl) + }); if (typeof $().collection === 'function') { - simpleCollection(); complexCollection(); mediaCollection(); } - imageModals(); menuTabs(); + $('.select2-simple').select2({ + width: '100%', + }); + + const select2entityModal = document.getElementById('select2entity_modal') + if (select2entityModal) { + select2entityModal.addEventListener('show.bs.modal', event => { + console.log('event', event); + const button = event.relatedTarget + const contentRoute = button.getAttribute('data-modal-content-route'); + $(select2entityModal).find('.modal-content').load(contentRoute, () => { + // form submission + $(select2entityModal).find('form').submit((formSubmitEvent) => { + formSubmitEvent.preventDefault(); + console.log($(select2entityModal).find('form').attr("action")); + console.log($(select2entityModal).find('form').serialize()); + $.post( + $(select2entityModal).find('form').attr("action"), + $(select2entityModal).find('form').serialize(), + (data) => { + if (data.success) { + $(select2entityModal).modal('hide'); + } else { + alert('There was a problem saving the form.'); + } + }, + ); + }); + // initialize select2entity, select2 simple, and tinymce for modal content + $(select2entityModal).find('.select2entity').select2entity(); + $(select2entityModal).find('.select2-simple').select2({ + width: '100%', + }); + if (tinymce && $(select2entityModal).find('.tinymce').length > 0 ) { + $(select2entityModal).find('.tinymce').each(function (index, textarea) { + tinymce.execCommand('mceRemoveEditor', false, textarea.id); + tinymce.execCommand("mceAddEditor", false, textarea.id); + }); + } + }); + }); + select2entityModal.addEventListener('hidden.bs.modal', () => { + // remove current modal content on hide + $(select2entityModal).find('.modal-content').html(''); + }); + } let showMoreContentList = [].slice.call(document.querySelectorAll('.show-more-content')) showMoreContentList.map((showMoreContentEl) => { @@ -230,6 +265,20 @@ setTimeout(updateAnchorScroll, 3000) setTimeout(updateAnchorScroll, 4000) } + + // fix collection required label + $('legend.required, label.required').each( (index, element) => { + if ($(element).next().hasClass('collection')) { + $(element).removeClass('required'); + } + }); + + // fix select2 not focusing properly on open https://stackoverflow.com/a/67691578 + $(document).on('select2:open', (e) => { + $(`.select2-search__field[aria-controls='select2-${e.target.id}-results']`).each((key, value) => { + value.focus(); + }); + }) }); })(jQuery, window); diff --git a/src/Command/ImportPodcastCommand.php b/src/Command/ImportPodcastCommand.php index d53f1fe..65aca14 100644 --- a/src/Command/ImportPodcastCommand.php +++ b/src/Command/ImportPodcastCommand.php @@ -20,11 +20,8 @@ use GuzzleHttp\Pool; use GuzzleHttp\Psr7\Response; use Nines\MediaBundle\Entity\Audio; -use Nines\MediaBundle\Entity\AudioContainerInterface; use Nines\MediaBundle\Entity\Image; -use Nines\MediaBundle\Entity\ImageContainerInterface; use Nines\MediaBundle\Entity\Pdf; -use Nines\MediaBundle\Entity\PdfContainerInterface; use Nines\MediaBundle\Service\AudioManager; use Nines\MediaBundle\Service\ImageManager; use Nines\MediaBundle\Service\PdfManager; @@ -354,13 +351,13 @@ private function processEpisodes() : void { $season = $this->seasons[$seasonNumber]; $episode->setSeason($season); - $episodeNumber = $episode->getNumber() ? $episode->getNumber() : (int) $this->getItemTagValue($item, SimplePie::NAMESPACE_ITUNES, 'episode'); + $episodeNumber = $episode->getNumber() ? (float) $episode->getNumber() : $this->getItemTagValue($item, SimplePie::NAMESPACE_ITUNES, 'episode'); if ($episodeNumber) { - $this->seasonEpisodeCounter[$seasonNumber][$episodeType] = $episodeNumber; + $this->seasonEpisodeCounter[$seasonNumber][$episodeType] = (int) $episodeNumber; } else { $episodeNumber = ++$this->seasonEpisodeCounter[$seasonNumber][$episodeType]; } - $episode->setNumber($episodeNumber); + $episode->setNumber((float) $episodeNumber); $this->output->writeln("- Season {$episode->getSeason()->getNumber()} Episode {$episode->getNumber()}"); @@ -674,7 +671,7 @@ protected function execute(InputInterface $input, OutputInterface $output) : int $this->seasons = []; $this->seasonEpisodeCounter = []; foreach ($this->podcast->getSeasons() as $season) { - if ($season->getNumber() > 0) { + if (null !== $season->getNumber()) { $this->seasons[$season->getNumber()] = $season; } } diff --git a/src/Config/ContributorRole.php b/src/Config/ContributorRole.php new file mode 100644 index 0000000..869a856 --- /dev/null +++ b/src/Config/ContributorRole.php @@ -0,0 +1,599 @@ + 'Abridger', + self::act => 'Actor', + self::adp => 'Adapter', + self::rcp => 'Addressee', + self::anl => 'Analyst', + self::anm => 'Animator', + self::ann => 'Annotator', + self::anc => 'Announcer', + self::apl => 'Appellant', + self::ape => 'Appellee', + self::app => 'Applicant', + self::arc => 'Architect', + self::arr => 'Arranger', + self::acp => 'Art copyist', + self::adi => 'Art director', + self::art => 'Artist', + self::ard => 'Artistic director', + self::asg => 'Assignee', + self::asn => 'Associated name', + self::att => 'Attributed name', + self::auc => 'Auctioneer', + self::aue => 'Audio engineer', + self::aup => 'Audio producer', + self::aut => 'Author', + self::aqt => 'Author in quotations or text abstracts', + self::aft => 'Author of afterword, colophon, etc.', + self::aud => 'Author of dialog', + self::aui => 'Author of introduction, etc.', + self::ato => 'Autographer', + self::ant => 'Bibliographic antecedent', + self::bnd => 'Binder', + self::bdd => 'Binding designer', + self::blw => 'Blurb writer', + self::bka => 'Book artist', + self::bkd => 'Book designer', + self::bkp => 'Book producer', + self::bjd => 'Bookjacket designer', + self::bpd => 'Bookplate designer', + self::bsl => 'Bookseller', + self::brl => 'Braille embosser', + self::brd => 'Broadcaster', + self::cll => 'Calligrapher', + self::cop => 'Camera operator', + self::ctg => 'Cartographer', + self::cas => 'Caster', + self::cad => 'Casting director', + self::cns => 'Censor', + self::chr => 'Choreographer', + self::cng => 'Cinematographer', + self::cli => 'Client', + self::cor => 'Collection registrar', + self::col => 'Collector', + self::clt => 'Collotyper', + self::clr => 'Colorist', + self::cmm => 'Commentator', + self::cwt => 'Commentator for written text', + self::com => 'Compiler', + self::cpl => 'Complainant', + self::cpt => 'Complainant-appellant', + self::cpe => 'Complainant-appellee', + self::cmp => 'Composer', + self::cmt => 'Compositor', + self::ccp => 'Conceptor', + self::cnd => 'Conductor', + self::con => 'Conservator', + self::csl => 'Consultant', + self::csp => 'Consultant to a project', + self::cos => 'Contestant', + self::cot => 'Contestant-appellant', + self::coe => 'Contestant-appellee', + self::cts => 'Contestee', + self::ctt => 'Contestee-appellant', + self::cte => 'Contestee-appellee', + self::ctr => 'Contractor', + self::ctb => 'Contributor', + self::cpc => 'Copyright claimant', + self::cph => 'Copyright holder', + self::crr => 'Corrector', + self::crp => 'Correspondent', + self::cst => 'Costume designer', + self::cou => 'Court governed', + self::crt => 'Court reporter', + self::cov => 'Cover designer', + self::cre => 'Creator', + self::cur => 'Curator', + self::dnc => 'Dancer', + self::dtc => 'Data contributor', + self::dtm => 'Data manager', + self::dte => 'Dedicatee', + self::dto => 'Dedicator', + self::dfd => 'Defendant', + self::dft => 'Defendant-appellant', + self::dfe => 'Defendant-appellee', + self::dgc => 'Degree committee member', + self::dgg => 'Degree granting institution', + self::dgs => 'Degree supervisor', + self::dln => 'Delineator', + self::dpc => 'Depicted', + self::dpt => 'Depositor', + self::dsr => 'Designer', + self::drt => 'Director', + self::dis => 'Dissertant', + self::dbp => 'Distribution place', + self::djo => 'DJ', + self::dnr => 'Donor', + self::drm => 'Draftsman', + self::dbd => 'Dubbing director', + self::dub => 'Dubious author', + self::edt => 'Editor', + self::edc => 'Editor of compilation', + self::edm => 'Editor of moving image work', + self::edd => 'Editorial director', + self::elg => 'Electrician', + self::elt => 'Electrotyper', + self::enj => 'Enacting jurisdiction', + self::eng => 'Engineer', + self::egr => 'Engraver', + self::etr => 'Etcher', + self::evp => 'Event place', + self::exp => 'Expert', + self::fac => 'Facsimilist', + self::fld => 'Field director', + self::fmd => 'Film director', + self::fds => 'Film distributor', + self::flm => 'Film editor', + self::fmp => 'Film producer', + self::fmk => 'Filmmaker', + self::fpy => 'First party', + self::frg => 'Forger', + self::fmo => 'Former owner', + self::fon => 'Founder', + self::fnd => 'Funder', + self::gis => 'Geographic information specialist', + self::hnr => 'Honoree', + self::hst => 'Host', + self::his => 'Host institution', + self::ilu => 'Illuminator', + self::ill => 'Illustrator', + self::ins => 'Inscriber', + self::itr => 'Instrumentalist', + self::ive => 'Interviewee', + self::ivr => 'Interviewer', + self::inv => 'Inventor', + self::isb => 'Issuing body', + self::jud => 'Judge', + self::jug => 'Jurisdiction governed', + self::lbr => 'Laboratory', + self::ldr => 'Laboratory director', + self::lsa => 'Landscape architect', + self::led => 'Lead', + self::len => 'Lender', + self::lil => 'Libelant', + self::lit => 'Libelant-appellant', + self::lie => 'Libelant-appellee', + self::lel => 'Libelee', + self::let => 'Libelee-appellant', + self::lee => 'Libelee-appellee', + self::lbt => 'Librettist', + self::lse => 'Licensee', + self::lso => 'Licensor', + self::lgd => 'Lighting designer', + self::ltg => 'Lithographer', + self::lyr => 'Lyricist', + self::mka => 'Makeup artist', + self::mfp => 'Manufacture place', + self::mfr => 'Manufacturer', + self::mrb => 'Marbler', + self::mrk => 'Markup editor', + self::med => 'Medium', + self::mdc => 'Metadata contact', + self::mte => 'Metal-engraver', + self::mtk => 'Minute taker', + self::mxe => 'Mixing engineer', + self::mod => 'Moderator', + self::mon => 'Monitor', + self::mcp => 'Music copyist', + self::mup => 'Music programmer', + self::msd => 'Musical director', + self::mus => 'Musician', + self::nrt => 'Narrator', + self::nan => 'News anchor', + self::onp => 'Onscreen participant', + self::osp => 'Onscreen presenter', + self::opn => 'Opponent', + self::orm => 'Organizer', + self::org => 'Originator', + self::oth => 'Other', + self::own => 'Owner', + self::pan => 'Panelist', + self::ppm => 'Papermaker', + self::pta => 'Patent applicant', + self::pth => 'Patent holder', + self::pat => 'Patron', + self::prf => 'Performer', + self::pma => 'Permitting agency', + self::pht => 'Photographer', + self::pad => 'Place of address', + self::ptf => 'Plaintiff', + self::ptt => 'Plaintiff-appellant', + self::pte => 'Plaintiff-appellee', + self::plt => 'Platemaker', + self::pra => 'Praeses', + self::pre => 'Presenter', + self::prt => 'Printer', + self::pop => 'Printer of plates', + self::prm => 'Printmaker', + self::prc => 'Process contact', + self::pro => 'Producer', + self::prn => 'Production company', + self::prs => 'Production designer', + self::pmn => 'Production manager', + self::prd => 'Production personnel', + self::prp => 'Production place', + self::prg => 'Programmer', + self::pdr => 'Project director', + self::pfr => 'Proofreader', + self::prv => 'Provider', + self::pup => 'Publication place', + self::pbl => 'Publisher', + self::pbd => 'Publishing director', + self::ppt => 'Puppeteer', + self::rdd => 'Radio director', + self::rpc => 'Radio producer', + self::rap => 'Rapporteur', + self::rce => 'Recording engineer', + self::rcd => 'Recordist', + self::red => 'Redaktor', + self::rxa => 'Remix artist', + self::ren => 'Renderer', + self::rpt => 'Reporter', + self::rps => 'Repository', + self::rth => 'Research team head', + self::rtm => 'Research team member', + self::res => 'Researcher', + self::rsp => 'Respondent', + self::rst => 'Respondent-appellant', + self::rse => 'Respondent-appellee', + self::rpy => 'Responsible party', + self::rsg => 'Restager', + self::rsr => 'Restorationist', + self::rev => 'Reviewer', + self::rbr => 'Rubricator', + self::sce => 'Scenarist', + self::sad => 'Scientific advisor', + self::aus => 'Screenwriter', + self::scr => 'Scribe', + self::scl => 'Sculptor', + self::spy => 'Second party', + self::sec => 'Secretary', + self::sll => 'Seller', + self::std => 'Set designer', + self::stg => 'Setting', + self::sgn => 'Signer', + self::sng => 'Singer', + self::swd => 'Software developer', + self::sds => 'Sound designer', + self::sde => 'Sound engineer', + self::spk => 'Speaker', + self::sfx => 'Special effects provider', + self::spn => 'Sponsor', + self::sgd => 'Stage director', + self::stm => 'Stage manager', + self::stn => 'Standards body', + self::str => 'Stereotyper', + self::stl => 'Storyteller', + self::sht => 'Supporting host', + self::srv => 'Surveyor', + self::tch => 'Teacher', + self::tcd => 'Technical director', + self::tld => 'Television director', + self::tlg => 'Television guest', + self::tlh => 'Television host', + self::tlp => 'Television producer', + self::tau => 'Television writer', + self::ths => 'Thesis advisor', + self::trc => 'Transcriber', + self::trl => 'Translator', + self::tyd => 'Type designer', + self::tyg => 'Typographer', + self::uvp => 'University place', + self::vdg => 'Videographer', + self::vfx => 'Visual effects provider', + self::vac => 'Voice actor', + self::wit => 'Witness', + self::wde => 'Wood engraver', + self::wdc => 'Woodcutter', + self::wam => 'Writer of accompanying material', + self::wac => 'Writer of added commentary', + self::wal => 'Writer of added lyrics', + self::wat => 'Writer of added text', + self::win => 'Writer of introduction', + self::wpr => 'Writer of preface', + self::wst => 'Writer of supplementary textual content', + }; + } +} diff --git a/src/Controller/ContributorRoleController.php b/src/Controller/ContributorRoleController.php deleted file mode 100644 index acf98b9..0000000 --- a/src/Controller/ContributorRoleController.php +++ /dev/null @@ -1,114 +0,0 @@ -query->get('q'); - $query = $q ? $contributorRoleRepository->searchQuery($q) : $contributorRoleRepository->indexQuery(); - - return [ - 'contributor_roles' => $this->paginator->paginate($query, $request->query->getInt('page', 1), $this->getParameter('page_size'), ['wrap-queries' => true]), - 'q' => $q, - ]; - } - - #[Route(path: '/typeahead', name: 'contributor_role_typeahead', methods: ['GET'])] - public function typeahead(Request $request, ContributorRoleRepository $contributorRoleRepository) : JsonResponse { - $q = $request->query->get('q'); - if ( ! $q) { - return new JsonResponse([]); - } - $data = []; - - foreach ($contributorRoleRepository->typeaheadQuery($q)->execute() as $result) { - $data[] = [ - 'id' => $result->getId(), - 'text' => (string) $result, - ]; - } - - return new JsonResponse($data); - } - - #[Route(path: '/new', name: 'contributor_role_new', methods: ['GET', 'POST'])] - #[Template] - public function new(EntityManagerInterface $entityManager, Request $request) : array|RedirectResponse { - $contributorRole = new ContributorRole(); - $form = $this->createForm(ContributorRoleType::class, $contributorRole); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->persist($contributorRole); - $entityManager->flush(); - $this->addFlash('success', 'Contributor role created successfully.'); - - return $this->redirectToRoute('contributor_role_show', ['id' => $contributorRole->getId()]); - } - - return [ - 'contributor_role' => $contributorRole, - 'form' => $form->createView(), - ]; - } - - #[Route(path: '/{id}', name: 'contributor_role_show', methods: ['GET'])] - #[Template] - public function show(ContributorRole $contributorRole) : array { - return [ - 'contributor_role' => $contributorRole, - ]; - } - - #[Route(path: '/{id}/edit', name: 'contributor_role_edit', methods: ['GET', 'POST'])] - #[Template] - public function edit(EntityManagerInterface $entityManager, Request $request, ContributorRole $contributorRole) : array|RedirectResponse { - $form = $this->createForm(ContributorRoleType::class, $contributorRole); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->flush(); - $this->addFlash('success', 'Contributor role updated successfully.'); - - return $this->redirectToRoute('contributor_role_show', ['id' => $contributorRole->getId()]); - } - - return [ - 'contributor_role' => $contributorRole, - 'form' => $form->createView(), - ]; - } - - #[Route(path: '/{id}', name: 'contributor_role_delete', methods: ['DELETE'])] - public function delete(EntityManagerInterface $entityManager, Request $request, ContributorRole $contributorRole) : RedirectResponse { - if ($this->isCsrfTokenValid('delete_contributor_role' . $contributorRole->getId(), $request->request->get('_token'))) { - $entityManager->remove($contributorRole); - $entityManager->flush(); - $this->addFlash('success', 'The contributorRole has been deleted.'); - } - - return $this->redirectToRoute('contributor_role_index'); - } -} diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 34e4ded..1619e9f 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -27,6 +27,5 @@ public function indexAction(Request $request) : RedirectResponse { #[Route(path: '/privacy', name: 'privacy')] #[Template] - public function privacyAction(Request $request) : void { - } + public function privacyAction(Request $request) : void {} } diff --git a/src/Controller/EpisodeController.php b/src/Controller/EpisodeController.php index 929c328..01537c2 100644 --- a/src/Controller/EpisodeController.php +++ b/src/Controller/EpisodeController.php @@ -62,7 +62,12 @@ public function new(EntityManagerInterface $entityManager, Request $request, Pod $entityManager->flush(); $this->addFlash('success', 'Episode created successfully.'); + if ($request->request->has('submitAndContinue')) { + return $this->redirectToRoute('episode_edit', ['podcast_id' => $podcast->getId(), 'id' => $episode->getId()]); + } + return $this->redirectToRoute('podcast_show', ['id' => $podcast->getId()]); + } return [ @@ -144,7 +149,12 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Po $entityManager->flush(); $this->addFlash('success', 'Episode updated successfully.'); + if ($request->request->has('submitAndContinue')) { + return $this->redirectToRoute('episode_edit', ['podcast_id' => $podcast->getId(), 'id' => $episode->getId()]); + } + return $this->redirectToRoute('episode_show', ['podcast_id' => $podcast->getId(), 'id' => $episode->getId()]); + } return [ diff --git a/src/Controller/InstitutionController.php b/src/Controller/InstitutionController.php deleted file mode 100644 index 01b86eb..0000000 --- a/src/Controller/InstitutionController.php +++ /dev/null @@ -1,114 +0,0 @@ -query->get('q'); - $query = $q ? $institutionRepository->searchQuery($q) : $institutionRepository->indexQuery(); - - return [ - 'institutions' => $this->paginator->paginate($query, $request->query->getInt('page', 1), $this->getParameter('page_size'), ['wrap-queries' => true]), - 'q' => $q, - ]; - } - - #[Route(path: '/typeahead', name: 'institution_typeahead', methods: ['GET'])] - public function typeahead(Request $request, InstitutionRepository $institutionRepository) : JsonResponse { - $q = $request->query->get('q'); - if ( ! $q) { - return new JsonResponse([]); - } - $data = []; - - foreach ($institutionRepository->typeaheadQuery($q)->execute() as $result) { - $data[] = [ - 'id' => $result->getId(), - 'text' => (string) $result, - ]; - } - - return new JsonResponse($data); - } - - #[Route(path: '/new', name: 'institution_new', methods: ['GET', 'POST'])] - #[Template] - public function new(EntityManagerInterface $entityManager, Request $request) : array|RedirectResponse { - $institution = new Institution(); - $form = $this->createForm(InstitutionType::class, $institution); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->persist($institution); - $entityManager->flush(); - $this->addFlash('success', 'Institution created successfully.'); - - return $this->redirectToRoute('institution_show', ['id' => $institution->getId()]); - } - - return [ - 'institution' => $institution, - 'form' => $form->createView(), - ]; - } - - #[Route(path: '/{id}', name: 'institution_show', methods: ['GET'])] - #[Template] - public function show(Institution $institution) : array { - return [ - 'institution' => $institution, - ]; - } - - #[Route(path: '/{id}/edit', name: 'institution_edit', methods: ['GET', 'POST'])] - #[Template] - public function edit(EntityManagerInterface $entityManager, Request $request, Institution $institution) : array|RedirectResponse { - $form = $this->createForm(InstitutionType::class, $institution); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->flush(); - $this->addFlash('success', 'Institution updated successfully.'); - - return $this->redirectToRoute('institution_show', ['id' => $institution->getId()]); - } - - return [ - 'institution' => $institution, - 'form' => $form->createView(), - ]; - } - - #[Route(path: '/{id}', name: 'institution_delete', methods: ['DELETE'])] - public function delete(EntityManagerInterface $entityManager, Request $request, Institution $institution) : RedirectResponse { - if ($this->isCsrfTokenValid('delete_institution' . $institution->getId(), $request->request->get('_token'))) { - $entityManager->remove($institution); - $entityManager->flush(); - $this->addFlash('success', 'The institution has been deleted.'); - } - - return $this->redirectToRoute('institution_index'); - } -} diff --git a/src/Controller/PersonController.php b/src/Controller/PersonController.php index c9a38ff..c458caf 100644 --- a/src/Controller/PersonController.php +++ b/src/Controller/PersonController.php @@ -5,45 +5,52 @@ namespace App\Controller; use App\Entity\Person; +use App\Entity\Podcast; use App\Form\PersonType; use App\Repository\PersonRepository; use Doctrine\ORM\EntityManagerInterface; use Knp\Bundle\PaginatorBundle\Definition\PaginatorAwareInterface; use Nines\UtilBundle\Controller\PaginatorTrait; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Requirement\Requirement; -#[Route(path: '/people')] -#[IsGranted('ROLE_USER')] +#[Route(path: '/podcasts/{podcast_id}/people', requirements: [ + 'podcast_id' => Requirement::DIGITS, +])] +#[ParamConverter('podcast', options: ['id' => 'podcast_id'])] +#[IsGranted('access', 'podcast')] class PersonController extends AbstractController implements PaginatorAwareInterface { use PaginatorTrait; #[Route(path: '', name: 'person_index', methods: ['GET'])] #[Template] - public function index(Request $request, PersonRepository $personRepository) : array { + public function index(Request $request, PersonRepository $personRepository, Podcast $podcast) : array { $q = $request->query->get('q'); - $query = $q ? $personRepository->searchQuery($q) : $personRepository->indexQuery(); + $query = $q ? $personRepository->searchQuery($podcast, $q) : $personRepository->indexQuery($podcast); return [ + 'podcast' => $podcast, 'people' => $this->paginator->paginate($query, $request->query->getInt('page', 1), $this->getParameter('page_size'), ['wrap-queries' => true]), 'q' => $q, ]; } #[Route(path: '/typeahead', name: 'person_typeahead', methods: ['GET'])] - public function typeahead(Request $request, PersonRepository $personRepository) : JsonResponse { + public function typeahead(Request $request, PersonRepository $personRepository, Podcast $podcast) : JsonResponse { $q = $request->query->get('q'); if ( ! $q) { - return new JsonResponse([]); + $q = '%'; } $data = []; - foreach ($personRepository->typeaheadQuery($q)->execute() as $result) { + foreach ($personRepository->typeaheadQuery($podcast, $q)->execute() as $result) { $data[] = [ 'id' => $result->getId(), 'text' => (string) $result, @@ -54,37 +61,36 @@ public function typeahead(Request $request, PersonRepository $personRepository) } #[Route(path: '/new', name: 'person_new', methods: ['GET', 'POST'])] - #[Template] - public function new(EntityManagerInterface $entityManager, Request $request) : array|RedirectResponse { + #[Template('person/new_modal_content.html.twig')] + public function new(EntityManagerInterface $entityManager, Request $request, Podcast $podcast) : array|JsonResponse { $person = new Person(); + $person->setPodcast($podcast); + $podcast->addAllPerson($person); + $form = $this->createForm(PersonType::class, $person); $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->persist($person); - $entityManager->flush(); - $this->addFlash('success', 'Person created successfully.'); + if ($form->isSubmitted()) { + if ($form->isValid()) { + $entityManager->persist($person); + $entityManager->flush(); + } - return $this->redirectToRoute('person_show', ['id' => $person->getId()]); + return new JsonResponse([ + 'success' => $form->isValid(), + ]); } return [ + 'podcast' => $podcast, 'person' => $person, 'form' => $form->createView(), ]; } - #[Route(path: '/{id}', name: 'person_show', methods: ['GET'])] - #[Template] - public function show(Person $person) : array { - return [ - 'person' => $person, - ]; - } - #[Route(path: '/{id}/edit', name: 'person_edit', methods: ['GET', 'POST'])] #[Template] - public function edit(EntityManagerInterface $entityManager, Request $request, Person $person) : array|RedirectResponse { + public function edit(EntityManagerInterface $entityManager, Request $request, Podcast $podcast, Person $person) : array|RedirectResponse { $form = $this->createForm(PersonType::class, $person); $form->handleRequest($request); @@ -92,7 +98,7 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Pe $entityManager->flush(); $this->addFlash('success', 'Person updated successfully.'); - return $this->redirectToRoute('person_show', ['id' => $person->getId()]); + return $this->redirectToRoute('person_index', ['podcast_id' => $podcast->getId()]); } return [ @@ -102,13 +108,13 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Pe } #[Route(path: '/{id}', name: 'person_delete', methods: ['DELETE'])] - public function delete(EntityManagerInterface $entityManager, Request $request, Person $person) : RedirectResponse { + public function delete(EntityManagerInterface $entityManager, Request $request, Podcast $podcast, Person $person) : RedirectResponse { if ($this->isCsrfTokenValid('delete_person' . $person->getId(), $request->request->get('_token'))) { $entityManager->remove($person); $entityManager->flush(); $this->addFlash('success', 'The person has been deleted.'); } - return $this->redirectToRoute('person_index'); + return $this->redirectToRoute('person_index', ['podcast_id' => $podcast->getId()]); } } diff --git a/src/Controller/PodcastController.php b/src/Controller/PodcastController.php index b5250ee..2966c3d 100644 --- a/src/Controller/PodcastController.php +++ b/src/Controller/PodcastController.php @@ -5,7 +5,6 @@ namespace App\Controller; use App\Entity\Podcast; -use App\Entity\Share; use App\Form\PodcastType; use App\Repository\PodcastRepository; use Doctrine\ORM\EntityManagerInterface; @@ -41,45 +40,6 @@ public function index(Request $request, PodcastRepository $podcastRepository) : ]; } - #[Route(path: '/new', name: 'podcast_new', methods: ['GET', 'POST'])] - #[Template] - public function new(EntityManagerInterface $entityManager, Request $request) : array|RedirectResponse { - $podcast = new Podcast(); - $form = $this->createForm(PodcastType::class, $podcast); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - foreach ($podcast->getContributions() as $contribution) { - $contribution->setPodcast($podcast); - $entityManager->persist($contribution); - } - // add one time owner permissions on user who created podcast - $share = new Share(); - $share->setPodcast($podcast); - $share->setUser($this->getUser()); - $podcast->addShare($share); - $entityManager->persist($share); - - $entityManager->persist($podcast); - $entityManager->flush(); - - foreach ($podcast->getImages() as $image) { - $image->setEntity($podcast); - $entityManager->persist($image); - } - $podcast->updateStatus(); - $entityManager->flush(); - $this->addFlash('success', 'Podcast created successfully.'); - - return $this->redirectToRoute('podcast_show', ['id' => $podcast->getId()]); - } - - return [ - 'podcast' => $podcast, - 'form' => $form->createView(), - ]; - } - #[Route(path: '/{id}', name: 'podcast_show', methods: ['GET'], requirements: [ 'id' => Requirement::DIGITS, ])] @@ -123,7 +83,12 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Po $entityManager->flush(); $this->addFlash('success', 'Podcast updated successfully.'); + if ($request->request->has('submitAndContinue')) { + return $this->redirectToRoute('podcast_edit', ['id' => $podcast->getId()]); + } + return $this->redirectToRoute('podcast_show', ['id' => $podcast->getId()]); + } return [ diff --git a/src/Controller/PublisherController.php b/src/Controller/PublisherController.php index 593f82f..da3eee4 100644 --- a/src/Controller/PublisherController.php +++ b/src/Controller/PublisherController.php @@ -4,6 +4,7 @@ namespace App\Controller; +use App\Entity\Podcast; use App\Entity\Publisher; use App\Form\PublisherType; use App\Repository\PublisherRepository; @@ -11,39 +12,45 @@ use Knp\Bundle\PaginatorBundle\Definition\PaginatorAwareInterface; use Nines\UtilBundle\Controller\PaginatorTrait; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Requirement\Requirement; -#[Route(path: '/publishers')] -#[IsGranted('ROLE_USER')] +#[Route(path: '/podcasts/{podcast_id}/publishers', requirements: [ + 'podcast_id' => Requirement::DIGITS, +])] +#[ParamConverter('podcast', options: ['id' => 'podcast_id'])] +#[IsGranted('access', 'podcast')] class PublisherController extends AbstractController implements PaginatorAwareInterface { use PaginatorTrait; #[Route(path: '', name: 'publisher_index', methods: ['GET'])] #[Template] - public function index(Request $request, PublisherRepository $publisherRepository) : array { + public function index(Request $request, PublisherRepository $publisherRepository, Podcast $podcast) : array { $q = $request->query->get('q'); - $query = $q ? $publisherRepository->searchQuery($q) : $publisherRepository->indexQuery(); + $query = $q ? $publisherRepository->searchQuery($podcast, $q) : $publisherRepository->indexQuery($podcast); return [ + 'podcast' => $podcast, 'publishers' => $this->paginator->paginate($query, $request->query->getInt('page', 1), $this->getParameter('page_size'), ['wrap-queries' => true]), 'q' => $q, ]; } #[Route(path: '/typeahead', name: 'publisher_typeahead', methods: ['GET'])] - public function typeahead(Request $request, PublisherRepository $publisherRepository) : JsonResponse { + public function typeahead(Request $request, PublisherRepository $publisherRepository, Podcast $podcast) : JsonResponse { $q = $request->query->get('q'); if ( ! $q) { - return new JsonResponse([]); + $q = '%'; } $data = []; - foreach ($publisherRepository->typeaheadQuery($q)->execute() as $result) { + foreach ($publisherRepository->typeaheadQuery($podcast, $q)->execute() as $result) { $data[] = [ 'id' => $result->getId(), 'text' => (string) $result, @@ -54,37 +61,36 @@ public function typeahead(Request $request, PublisherRepository $publisherReposi } #[Route(path: '/new', name: 'publisher_new', methods: ['GET', 'POST'])] - #[Template] - public function new(EntityManagerInterface $entityManager, Request $request) : array|RedirectResponse { + #[Template('publisher/new_modal_content.html.twig')] + public function new(EntityManagerInterface $entityManager, Request $request, Podcast $podcast) : array|JsonResponse { $publisher = new Publisher(); + $publisher->setPodcast($podcast); + $podcast->addAllPublisher($publisher); + $form = $this->createForm(PublisherType::class, $publisher); $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->persist($publisher); - $entityManager->flush(); - $this->addFlash('success', 'Publisher created successfully.'); + if ($form->isSubmitted()) { + if ($form->isValid()) { + $entityManager->persist($publisher); + $entityManager->flush(); + } - return $this->redirectToRoute('publisher_show', ['id' => $publisher->getId()]); + return new JsonResponse([ + 'success' => $form->isValid(), + ]); } return [ + 'podcast' => $podcast, 'publisher' => $publisher, 'form' => $form->createView(), ]; } - #[Route(path: '/{id}', name: 'publisher_show', methods: ['GET'])] - #[Template] - public function show(Publisher $publisher) : array { - return [ - 'publisher' => $publisher, - ]; - } - #[Route(path: '/{id}/edit', name: 'publisher_edit', methods: ['GET', 'POST'])] #[Template] - public function edit(EntityManagerInterface $entityManager, Request $request, Publisher $publisher) : array|RedirectResponse { + public function edit(EntityManagerInterface $entityManager, Request $request, Podcast $podcast, Publisher $publisher) : array|RedirectResponse { $form = $this->createForm(PublisherType::class, $publisher); $form->handleRequest($request); @@ -92,7 +98,7 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Pu $entityManager->flush(); $this->addFlash('success', 'Publisher updated successfully.'); - return $this->redirectToRoute('publisher_show', ['id' => $publisher->getId()]); + return $this->redirectToRoute('publisher_index', ['podcast_id' => $podcast->getId()]); } return [ @@ -102,13 +108,13 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Pu } #[Route(path: '/{id}', name: 'publisher_delete', methods: ['DELETE'])] - public function delete(EntityManagerInterface $entityManager, Request $request, Publisher $publisher) : RedirectResponse { + public function delete(EntityManagerInterface $entityManager, Request $request, Podcast $podcast, Publisher $publisher) : RedirectResponse { if ($this->isCsrfTokenValid('delete_publisher' . $publisher->getId(), $request->request->get('_token'))) { $entityManager->remove($publisher); $entityManager->flush(); $this->addFlash('success', 'The publisher has been deleted.'); } - return $this->redirectToRoute('publisher_index'); + return $this->redirectToRoute('publisher_index', ['podcast_id' => $podcast->getId()]); } } diff --git a/src/Controller/SeasonController.php b/src/Controller/SeasonController.php index 38ce1c1..51a80dc 100644 --- a/src/Controller/SeasonController.php +++ b/src/Controller/SeasonController.php @@ -34,7 +34,7 @@ class SeasonController extends AbstractController implements PaginatorAwareInter public function typeahead(Request $request, SeasonRepository $seasonRepository, Podcast $podcast) : JsonResponse { $q = $request->query->get('q'); if ( ! $q) { - return new JsonResponse([]); + $q = '%'; } $data = []; @@ -74,7 +74,12 @@ public function new(EntityManagerInterface $entityManager, Request $request, Pod $entityManager->flush(); $this->addFlash('success', 'Season created successfully.'); + if ($request->request->has('submitAndContinue')) { + return $this->redirectToRoute('season_edit', ['podcast_id' => $podcast->getId(), 'id' => $season->getId()]); + } + return $this->redirectToRoute('podcast_show', ['id' => $podcast->getId()]); + } return [ @@ -132,7 +137,12 @@ public function edit(EntityManagerInterface $entityManager, Request $request, Po $entityManager->flush(); $this->addFlash('success', 'Season updated successfully.'); + if ($request->request->has('submitAndContinue')) { + return $this->redirectToRoute('season_edit', ['podcast_id' => $podcast->getId(), 'id' => $season->getId()]); + } + return $this->redirectToRoute('season_show', ['podcast_id' => $podcast->getId(), 'id' => $season->getId()]); + } return [ diff --git a/src/Controller/ShareController.php b/src/Controller/ShareController.php index 1cbd351..1f4fa9a 100644 --- a/src/Controller/ShareController.php +++ b/src/Controller/ShareController.php @@ -89,7 +89,7 @@ public function delete(EntityManagerInterface $entityManager, Request $request, public function typeahead(Request $request, UserRepository $userRepository, Podcast $podcast) : JsonResponse { $q = $request->query->get('q'); if ( ! $q) { - return new JsonResponse([]); + $q = '%'; } $data = []; diff --git a/src/DataFixtures/ContributionFixtures.php b/src/DataFixtures/ContributionFixtures.php index 730d86b..b62b335 100644 --- a/src/DataFixtures/ContributionFixtures.php +++ b/src/DataFixtures/ContributionFixtures.php @@ -4,6 +4,7 @@ namespace App\DataFixtures; +use App\Config\ContributorRole; use App\Entity\Contribution; use DateTimeImmutable; use Doctrine\Bundle\FixturesBundle\Fixture; @@ -18,11 +19,12 @@ public static function getGroups() : array { public function load(ObjectManager $em) : void { for ($i = 0; $i < 8; $i++) { - $fixture = new Contribution(); + $podcast = $this->getReference($i < 4 ? 'podcast.1' : 'podcast.5'); - $fixture->setPerson($this->getReference('person.1')); - $fixture->setContributorrole($this->getReference('contributorrole.1')); - $fixture->setPodcast($this->getReference($i < 4 ? 'podcast.1' : 'podcast.5')); + $fixture = new Contribution(); + $fixture->setPerson($podcast->getAllPeople()[$i % 4]); + $fixture->setRoles([ContributorRole::hst]); + $fixture->setPodcast($podcast); $fixture->setSeason($this->getReference($i < 4 ? 'season.1' : 'season.5')); $fixture->setEpisode($this->getReference($i < 4 ? 'episode.1' : 'episode.5')); $fixture->setCreated(new DateTimeImmutable('2023-05-25')); @@ -36,8 +38,6 @@ public function load(ObjectManager $em) : void { public function getDependencies() { return [ - PersonFixtures::class, - ContributorRoleFixtures::class, PodcastFixtures::class, SeasonFixtures::class, EpisodeFixtures::class, diff --git a/src/DataFixtures/ContributorRoleFixtures.php b/src/DataFixtures/ContributorRoleFixtures.php deleted file mode 100644 index 4a0b676..0000000 --- a/src/DataFixtures/ContributorRoleFixtures.php +++ /dev/null @@ -1,32 +0,0 @@ -setName('Name ' . $i); - $fixture->setLabel('Label ' . $i); - $fixture->setDescription("

This is paragraph {$i}

"); - $fixture->setCreated(new DateTimeImmutable('2023-05-25')); - $fixture->setUpdated(new DateTimeImmutable('2023-05-25')); - $em->persist($fixture); - $this->setReference('contributorrole.' . $i, $fixture); - } - - $em->flush(); - } -} diff --git a/src/DataFixtures/EpisodeFixtures.php b/src/DataFixtures/EpisodeFixtures.php index a4fed78..9e53c8b 100644 --- a/src/DataFixtures/EpisodeFixtures.php +++ b/src/DataFixtures/EpisodeFixtures.php @@ -66,7 +66,7 @@ public function load(ObjectManager $em) : void { } elseif (3 === $i % 4) { $fixture->setEpisodeType('trailer'); } - $fixture->setNumber($i); + $fixture->setNumber((float) $i); $fixture->setDate(new DateTimeImmutable("2020-{$i}-{$i}")); $fixture->setRunTime("00:{$i}5:00"); $fixture->setTitle('Title ' . $i); diff --git a/src/DataFixtures/ExportFixtures.php b/src/DataFixtures/ExportFixtures.php index cd14f30..3208708 100644 --- a/src/DataFixtures/ExportFixtures.php +++ b/src/DataFixtures/ExportFixtures.php @@ -15,8 +15,7 @@ class ExportFixtures extends Fixture implements DependentFixtureInterface, FixtureGroupInterface { public function __construct( private Filesystem $filesystem, - ) { - } + ) {} public static function getGroups() : array { return ['dev', 'test']; diff --git a/src/DataFixtures/InstitutionFixtures.php b/src/DataFixtures/InstitutionFixtures.php deleted file mode 100644 index 9b5dc6a..0000000 --- a/src/DataFixtures/InstitutionFixtures.php +++ /dev/null @@ -1,32 +0,0 @@ -setCountry('Country ' . $i); - $fixture->setName('Name ' . $i); - $fixture->setCreated(new DateTimeImmutable('2023-05-25')); - $fixture->setUpdated(new DateTimeImmutable('2023-05-25')); - - $em->persist($fixture); - $this->setReference('institution.' . $i, $fixture); - } - - $em->flush(); - } -} diff --git a/src/DataFixtures/PersonFixtures.php b/src/DataFixtures/PersonFixtures.php deleted file mode 100644 index bb112e8..0000000 --- a/src/DataFixtures/PersonFixtures.php +++ /dev/null @@ -1,49 +0,0 @@ -setFullname('Fullname ' . $i); - $fixture->setSortableName('SortableName ' . $i); - $fixture->setLocation('Location ' . $i); - $fixture->setBio("

This is paragraph {$i}

"); - $fixture->setCreated(new DateTimeImmutable('2023-05-25')); - $fixture->setUpdated(new DateTimeImmutable('2023-05-25')); - $fixture->setInstitution($this->getReference('institution.1')); - $em->persist($fixture); - $this->setReference('person.' . $i, $fixture); - $em->flush(); - - $link = new Link(); - $link->setUrl('http://example.com/' . $i); - $em->persist($link); - $fixture->setLinks([$link]); - $em->flush(); - } - - $em->flush(); - } - - public function getDependencies() { - return [ - InstitutionFixtures::class, - ]; - } -} diff --git a/src/DataFixtures/PodcastFixtures.php b/src/DataFixtures/PodcastFixtures.php index 0a25783..0083d27 100644 --- a/src/DataFixtures/PodcastFixtures.php +++ b/src/DataFixtures/PodcastFixtures.php @@ -4,7 +4,10 @@ namespace App\DataFixtures; +use App\Entity\Person; use App\Entity\Podcast; + +use App\Entity\Publisher; use App\Entity\Share; use DateTimeImmutable; use Doctrine\Bundle\FixturesBundle\Fixture; @@ -46,10 +49,42 @@ public function load(ObjectManager $em) : void { $fixture->addKeyword('Keyword ' . $i); $fixture->setCreated(new DateTimeImmutable('2023-05-25')); $fixture->setUpdated(new DateTimeImmutable('2023-05-25')); - $fixture->setPublisher($this->getReference('publisher.1')); $em->persist($fixture); $em->flush(); + for ($j = 0; $j < 4; $j++) { + $person = new Person(); + $person->setPodcast($fixture); + $person->setFullname('Fullname ' . $i . ' - ' . $j); + $person->setSortableName('SortableName ' . $i . ' - ' . $j); + $person->setLocation('Location ' . $i . ' - ' . $j); + $person->setBio("

This is paragraph {$i} - {$j}

"); + $person->setCreated(new DateTimeImmutable('2023-05-25')); + $person->setUpdated(new DateTimeImmutable('2023-05-25')); + $person->setInstitution('Institution ' . $i . ' - ' . $j); + $em->persist($person); + $em->flush(); + } + + for ($j = 0; $j < 4; $j++) { + $publisher = new Publisher(); + $publisher->setPodcast($fixture); + $publisher->setName('Name ' . $i . ' - ' . $j); + $publisher->setLocation('Location ' . $i . ' - ' . $j); + $publisher->setWebsite('Website ' . $i . ' - ' . $j); + $publisher->setDescription("

This is paragraph {$i} - {$j}

"); + $publisher->setContact("

This is paragraph {$i} - {$j}

"); + $publisher->setCreated(new DateTimeImmutable('2023-05-25')); + $publisher->setUpdated(new DateTimeImmutable('2023-05-25')); + $em->persist($publisher); + $em->flush(); + + if ($j == 0) { + $fixture->setPublisher($publisher); + $em->flush(); + } + } + $imageFile = self::IMAGE_FILES[$i % 4]; $upload = new UploadedFile(dirname(__FILE__, 3) . '/tests/data/image/' . $imageFile, $imageFile, 'image/jpeg', null, true); $image = new Image(); @@ -78,7 +113,6 @@ public function load(ObjectManager $em) : void { public function getDependencies() { return [ - PublisherFixtures::class, UserFixtures::class, UserExtraFixtures::class, ]; diff --git a/src/DataFixtures/PublisherFixtures.php b/src/DataFixtures/PublisherFixtures.php deleted file mode 100644 index f6497eb..0000000 --- a/src/DataFixtures/PublisherFixtures.php +++ /dev/null @@ -1,34 +0,0 @@ -setName('Name ' . $i); - $fixture->setLocation('Location ' . $i); - $fixture->setWebsite('Website ' . $i); - $fixture->setDescription("

This is paragraph {$i}

"); - $fixture->setContact("

This is paragraph {$i}

"); - $fixture->setCreated(new DateTimeImmutable('2023-05-25')); - $fixture->setUpdated(new DateTimeImmutable('2023-05-25')); - $em->persist($fixture); - $this->setReference('publisher.' . $i, $fixture); - } - - $em->flush(); - } -} diff --git a/src/DataFixtures/SeasonFixtures.php b/src/DataFixtures/SeasonFixtures.php index ea6c874..61cfb22 100644 --- a/src/DataFixtures/SeasonFixtures.php +++ b/src/DataFixtures/SeasonFixtures.php @@ -37,13 +37,15 @@ public function setImageManager(ImageManager $imageManager) : void { public function load(ObjectManager $em) : void { $this->imageManager->setCopy(true); for ($i = 0; $i < 8; $i++) { + $podcast = $this->getReference($i < 4 ? 'podcast.1' : 'podcast.5'); + $fixture = new Season(); $fixture->setNumber($i); $fixture->setTitle('Title ' . $i); $fixture->setSubTitle('SubTitle ' . $i); $fixture->setDescription("

This is paragraph {$i}

"); - $fixture->setPodcast($this->getReference($i < 4 ? 'podcast.1' : 'podcast.5')); - $fixture->setPublisher($this->getReference('publisher.1')); + $fixture->setPodcast($podcast); + $fixture->setPublisher($podcast->getAllPublishers()[0]); $fixture->setCreated(new DateTimeImmutable('2023-05-25')); $fixture->setUpdated(new DateTimeImmutable('2023-05-25')); $em->persist($fixture); @@ -72,7 +74,6 @@ public function load(ObjectManager $em) : void { public function getDependencies() { return [ PodcastFixtures::class, - PublisherFixtures::class, ]; } } diff --git a/src/Entity/Contribution.php b/src/Entity/Contribution.php index 56b984d..3ea0d63 100644 --- a/src/Entity/Contribution.php +++ b/src/Entity/Contribution.php @@ -4,27 +4,30 @@ namespace App\Entity; +use App\Config\ContributorRole; use App\Repository\ContributionRepository; use Doctrine\ORM\Mapping as ORM; use Nines\UtilBundle\Entity\AbstractEntity; #[ORM\Entity(repositoryClass: ContributionRepository::class)] class Contribution extends AbstractEntity { + #[ORM\Column(type: 'json', options: ['default' => '[]'])] + private ?array $roles = []; + #[ORM\ManyToOne(targetEntity: 'Person', inversedBy: 'contributions')] #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] private ?Person $person = null; - #[ORM\ManyToOne(targetEntity: 'ContributorRole', inversedBy: 'contributions')] - #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] - private ?ContributorRole $contributorRole = null; - #[ORM\ManyToOne(targetEntity: 'Podcast', inversedBy: 'contributions')] + #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')] private ?Podcast $podcast = null; #[ORM\ManyToOne(targetEntity: 'Season', inversedBy: 'contributions')] + #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')] private ?Season $season = null; #[ORM\ManyToOne(targetEntity: 'Episode', inversedBy: 'contributions')] + #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')] private ?Episode $episode = null; public function __construct() { @@ -32,7 +35,7 @@ public function __construct() { } public function __toString() : string { - return implode(',', [$this->person, $this->contributorRole, $this->podcast, $this->season, $this->episode]); + return implode(',', array_merge([$this->person, $this->podcast, $this->season, $this->episode], $this->getRoleLabels())); } public function getPerson() : ?Person { @@ -45,12 +48,38 @@ public function setPerson(?Person $person) : self { return $this; } - public function getContributorRole() : ?ContributorRole { - return $this->contributorRole; + public function getRoles() : array { + $roles = []; + foreach ($this->roles as $role) { + $roles[] = ContributorRole::from($role); + } + + return $roles; + } + + public function getRoleLabels() : array { + $roles = []; + foreach ($this->roles as $role) { + $roles[] = ContributorRole::from($role)->label(); + } + + return $roles; + } + + public function getRoleValues() : array { + $roles = []; + foreach ($this->roles as $role) { + $roles[] = ContributorRole::from($role)->value; + } + + return $roles; } - public function setContributorRole(?ContributorRole $contributorRole) : self { - $this->contributorRole = $contributorRole; + public function setRoles(array $roles) : self { + $this->roles = []; + foreach ($roles as $contributorRole) { + $this->roles[] = $contributorRole->value; + } return $this; } diff --git a/src/Entity/ContributorRole.php b/src/Entity/ContributorRole.php deleted file mode 100644 index 198d5a1..0000000 --- a/src/Entity/ContributorRole.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ - #[ORM\OneToMany(targetEntity: 'Contribution', mappedBy: 'contributorRole')] - private $contributions; - - public function __construct() { - parent::__construct(); - $this->contributions = new ArrayCollection(); - } - - /** - * @return Collection - */ - public function getContributions() : Collection { - return $this->contributions; - } - - public function addContribution(Contribution $contribution) : self { - if ( ! $this->contributions->contains($contribution)) { - $this->contributions[] = $contribution; - $contribution->setContributorRole($this); - } - - return $this; - } - - public function removeContribution(Contribution $contribution) : self { - if ($this->contributions->contains($contribution)) { - $this->contributions->removeElement($contribution); - // set the owning side to null (unless already changed) - if ($contribution->getContributorRole() === $this) { - $contribution->setContributorRole(null); - } - } - - return $this; - } - - public function getRelatorTerm() : ?string { - return $this->relatorTerm; - } - - public function setRelatorTerm(?string $relatorTerm) : self { - $this->relatorTerm = $relatorTerm; - - return $this; - } -} diff --git a/src/Entity/Episode.php b/src/Entity/Episode.php index 55a6ead..770fd6c 100644 --- a/src/Entity/Episode.php +++ b/src/Entity/Episode.php @@ -36,8 +36,8 @@ class Episode extends AbstractEntity implements ImageContainerInterface, AudioCo #[ORM\Column(type: 'string', length: 255, nullable: false, options: ['default' => 'full'])] private ?string $episodeType = null; - #[ORM\Column(type: 'integer')] - private ?int $number = null; + #[ORM\Column(type: 'float')] + private ?float $number = null; #[ORM\Column(type: 'date')] private ?DateTimeInterface $date = null; @@ -197,11 +197,11 @@ public function setEpisodeType(string $episodeType) : self { return $this; } - public function getNumber() : ?int { + public function getNumber() : ?float { return $this->number; } - public function setNumber(int $number) : self { + public function setNumber(float $number) : self { $this->number = $number; return $this; @@ -209,14 +209,15 @@ public function setNumber(int $number) : self { public function getSlug() : string { $seasonSlug = $this->season?->getId() ? $this->season->getSlug() : ''; + $episodeNumber = (float) $this->number; // removes trailing zeros if not needed if ('bonus' === $this->getEpisodeType()) { - return $seasonSlug . sprintf('B%02d', $this->number); + return "{$seasonSlug}B{$episodeNumber}"; } if ('trailer' === $this->getEpisodeType()) { - return $seasonSlug . sprintf('T%02d', $this->number); + return "{$seasonSlug}T{$episodeNumber}"; } - return $seasonSlug . sprintf('E%02d', $this->number); + return "{$seasonSlug}E{$episodeNumber}"; } public function getDate() : ?DateTimeInterface { @@ -363,20 +364,6 @@ public function getContributions() : Collection { return $this->contributions; } - public function getContributionsGroupedByPerson() : array { - $contributions = []; - - foreach ($this->contributions as $contribution) { - $person = $contribution->getPerson(); - if ( ! array_key_exists($person->getId(), $contributions)) { - $contributions[$person->getId()] = []; - } - $contributions[$person->getId()][] = $contribution; - } - - return $contributions; - } - public function addContribution(Contribution $contribution) : self { if ( ! $this->contributions->contains($contribution)) { $this->contributions[] = $contribution; diff --git a/src/Entity/Institution.php b/src/Entity/Institution.php deleted file mode 100644 index d07b083..0000000 --- a/src/Entity/Institution.php +++ /dev/null @@ -1,86 +0,0 @@ - - */ - #[ORM\OneToMany(targetEntity: 'App\Entity\Person', mappedBy: 'institution')] - private $people; - - public function __construct() { - parent::__construct(); - $this->people = new ArrayCollection(); - } - - public function __toString() : string { - return $this->name; - } - - public function getCountry() : ?string { - return $this->country; - } - - public function setCountry(string $country) : self { - $this->country = $country; - - return $this; - } - - public function getName() : ?string { - return $this->name; - } - - public function setName(string $name) : self { - $this->name = $name; - - return $this; - } - - /** - * @return Collection - */ - public function getPeople() : Collection { - return $this->people; - } - - public function addPerson(Person $person) : self { - if ( ! $this->people->contains($person)) { - $this->people[] = $person; - $person->setInstitution($this); - } - - return $this; - } - - public function removePerson(Person $person) : self { - if ($this->people->contains($person)) { - $this->people->removeElement($person); - // set the owning side to null (unless already changed) - if ($person->getInstitution() === $this) { - $person->setInstitution(null); - } - } - - return $this; - } -} diff --git a/src/Entity/Person.php b/src/Entity/Person.php index a1dea63..82f2dd6 100644 --- a/src/Entity/Person.php +++ b/src/Entity/Person.php @@ -8,17 +8,11 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Nines\MediaBundle\Entity\LinkableInterface; -use Nines\MediaBundle\Entity\LinkableTrait; use Nines\UtilBundle\Entity\AbstractEntity; #[ORM\Entity(repositoryClass: PersonRepository::class)] #[ORM\Index(name: 'person_ft', columns: ['fullname', 'bio'], flags: ['fulltext'])] -class Person extends AbstractEntity implements LinkableInterface { - use LinkableTrait { - LinkableTrait::__construct as linkable_constructor; - } - +class Person extends AbstractEntity { #[ORM\Column(type: 'string')] private ?string $fullname = null; @@ -31,8 +25,12 @@ class Person extends AbstractEntity implements LinkableInterface { #[ORM\Column(type: 'text')] private ?string $bio = null; - #[ORM\ManyToOne(targetEntity: 'App\Entity\Institution', inversedBy: 'people')] - private ?Institution $institution = null; + #[ORM\Column(type: 'string', nullable: true)] + private ?string $institution = null; + + #[ORM\ManyToOne(targetEntity: 'Podcast', inversedBy: 'allPeople')] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + private ?Podcast $podcast = null; /** * @var Collection @@ -42,7 +40,6 @@ class Person extends AbstractEntity implements LinkableInterface { public function __construct() { parent::__construct(); - $this->linkable_constructor(); $this->contributions = new ArrayCollection(); } @@ -101,6 +98,26 @@ public function setBio(string $bio) : self { return $this; } + public function getInstitution() : ?string { + return $this->institution; + } + + public function setInstitution(?string $institution) : self { + $this->institution = $institution; + + return $this; + } + + public function getPodcast() : ?Podcast { + return $this->podcast; + } + + public function setPodcast(?Podcast $podcast) : self { + $this->podcast = $podcast; + + return $this; + } + /** * @return Collection */ @@ -128,14 +145,4 @@ public function removeContribution(Contribution $contribution) : self { return $this; } - - public function getInstitution() : ?Institution { - return $this->institution; - } - - public function setInstitution(?Institution $institution) : self { - $this->institution = $institution; - - return $this; - } } diff --git a/src/Entity/Podcast.php b/src/Entity/Podcast.php index 4b67802..e254949 100644 --- a/src/Entity/Podcast.php +++ b/src/Entity/Podcast.php @@ -55,6 +55,7 @@ class Podcast extends AbstractEntity implements ImageContainerInterface { private ?string $rss = null; #[ORM\ManyToOne(targetEntity: 'Publisher', inversedBy: 'podcasts')] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] #[ORM\OrderBy(['name' => 'ASC', 'id' => 'ASC'])] private ?Publisher $publisher = null; @@ -78,7 +79,7 @@ class Podcast extends AbstractEntity implements ImageContainerInterface { * @var Collection */ #[ORM\OneToMany(targetEntity: 'Contribution', mappedBy: 'podcast', cascade: ['remove'])] - #[ORM\OrderBy(['person' => 'ASC', 'contributorRole' => 'ASC'])] + #[ORM\OrderBy(['person' => 'ASC'])] private $contributions; /** @@ -109,6 +110,20 @@ class Podcast extends AbstractEntity implements ImageContainerInterface { #[ORM\OrderBy(['created' => 'DESC', 'id' => 'DESC'])] private $imports; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: 'Person', mappedBy: 'podcast', cascade: ['remove'])] + #[ORM\OrderBy(['sortableName' => 'ASC', 'id' => 'ASC'])] + private $allPeople; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: 'Publisher', mappedBy: 'podcast', cascade: ['remove'])] + #[ORM\OrderBy(['name' => 'ASC', 'id' => 'ASC'])] + private $allPublishers; + protected static $ITUNES_CATEGORIES = [ 'Arts', 'Arts - Books', @@ -231,6 +246,8 @@ public function __construct() { $this->episodes = new ArrayCollection(); $this->exports = new ArrayCollection(); $this->imports = new ArrayCollection(); + $this->allPeople = new ArrayCollection(); + $this->allPublishers = new ArrayCollection(); } public function __toString() : string { @@ -425,20 +442,6 @@ public function getContributions() : Collection { return $this->contributions; } - public function getContributionsGroupedByPerson() : array { - $contributions = []; - - foreach ($this->contributions as $contribution) { - $person = $contribution->getPerson(); - if ( ! array_key_exists($person->getId(), $contributions)) { - $contributions[$person->getId()] = []; - } - $contributions[$person->getId()][] = $contribution; - } - - return $contributions; - } - public function addContribution(Contribution $contribution) : self { if ( ! $this->contributions->contains($contribution)) { $this->contributions[] = $contribution; @@ -732,6 +735,62 @@ public function getActiveImports() : Collection { return $this->imports->matching(new Criteria($expression)); } + /** + * @return Collection + */ + public function getAllPeople() : Collection { + return $this->allPeople; + } + + public function addAllPerson(Person $person) : self { + if ( ! $this->allPeople->contains($person)) { + $this->allPeople[] = $person; + $person->setPodcast($this); + } + + return $this; + } + + public function removeAllPerson(Person $person) : self { + if ($this->allPeople->contains($person)) { + $this->allPeople->removeElement($person); + // set the owning side to null (unless already changed) + if ($person->getPodcast() === $this) { + $person->setPodcast(null); + } + } + + return $this; + } + + /** + * @return Collection + */ + public function getAllPublishers() : Collection { + return $this->allPublishers; + } + + public function addAllPublisher(Publisher $publisher) : self { + if ( ! $this->allPublishers->contains($publisher)) { + $this->allPublishers[] = $publisher; + $publisher->setPodcast($this); + } + + return $this; + } + + public function removeAllPublisher(Publisher $publisher) : self { + if ($this->allPublishers->contains($publisher)) { + $this->allPublishers->removeElement($publisher); + // set the owning side to null (unless already changed) + if ($publisher->getPodcast() === $this) { + $publisher->setPodcast(null); + } + } + + return $this; + } + public function hasActiveImport() : ?bool { return ! $this->getActiveImports()->isEmpty(); } diff --git a/src/Entity/Publisher.php b/src/Entity/Publisher.php index ecbea8f..3e25796 100644 --- a/src/Entity/Publisher.php +++ b/src/Entity/Publisher.php @@ -28,6 +28,10 @@ class Publisher extends AbstractEntity { #[ORM\Column(type: 'text')] private ?string $contact = null; + #[ORM\ManyToOne(targetEntity: 'Podcast', inversedBy: 'allPublishers')] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + private ?Podcast $podcast = null; + /** * @var Collection */ @@ -100,6 +104,16 @@ public function setContact(string $contact) : self { return $this; } + public function getPodcast() : ?Podcast { + return $this->podcast; + } + + public function setPodcast(?Podcast $podcast) : self { + $this->podcast = $podcast; + + return $this; + } + /** * @return Collection */ diff --git a/src/Entity/Season.php b/src/Entity/Season.php index b84f310..d352d9d 100644 --- a/src/Entity/Season.php +++ b/src/Entity/Season.php @@ -36,7 +36,7 @@ class Season extends AbstractEntity implements ImageContainerInterface { private ?Podcast $podcast = null; #[ORM\ManyToOne(targetEntity: 'Publisher', inversedBy: 'seasons')] - #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] private ?Publisher $publisher = null; #[ORM\Column(type: 'json', options: ['default' => '[]'])] @@ -100,7 +100,7 @@ public function updateStatus() : void { } public function getSlug() : string { - return sprintf('S%02d', $this->number); + return "S{$this->number}"; } public function getNumber() : ?int { @@ -205,20 +205,6 @@ public function getContributions() : Collection { return $this->contributions; } - public function getContributionsGroupedByPerson() : array { - $contributions = []; - - foreach ($this->contributions as $contribution) { - $person = $contribution->getPerson(); - if ( ! array_key_exists($person->getId(), $contributions)) { - $contributions[$person->getId()] = []; - } - $contributions[$person->getId()][] = $contribution; - } - - return $contributions; - } - public function addContribution(Contribution $contribution) : self { if ( ! $this->contributions->contains($contribution)) { $this->contributions[] = $contribution; diff --git a/src/Form/ContributionType.php b/src/Form/ContributionType.php index d7d9e22..9d1828a 100644 --- a/src/Form/ContributionType.php +++ b/src/Form/ContributionType.php @@ -4,44 +4,56 @@ namespace App\Form; +use App\Config\ContributorRole; use App\Entity\Contribution; -use App\Entity\ContributorRole; use App\Entity\Person; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Tetranz\Select2EntityBundle\Form\Type\Select2EntityType; /** * Contribution form. */ class ContributionType extends AbstractType { + public function __construct( + public UrlGeneratorInterface $router, + ) {} + /** * Add form fields to $builder. */ public function buildForm(FormBuilderInterface $builder, array $options) : void { $builder->add('person', Select2EntityType::class, [ 'label' => 'Person', + 'required' => true, 'class' => Person::class, 'remote_route' => 'person_typeahead', + 'remote_params' => ['podcast_id' => $options['podcast']->getId()], 'allow_clear' => true, 'attr' => [ - 'add_path' => 'person_new', + 'add_route' => $this->router->generate('person_new', ['podcast_id' => $options['podcast']->getId()]), 'add_label' => 'Add Person', + 'add_modal' => true, ], 'placeholder' => 'Search for an existing person by name', ]); - $builder->add('contributorRole', Select2EntityType::class, [ - 'label' => 'Role', + $builder->add('roles', EnumType::class, [ + 'label' => 'Roles', + 'required' => true, + 'multiple' => true, 'class' => ContributorRole::class, - 'remote_route' => 'contributor_role_typeahead', - 'allow_clear' => true, + 'choice_label' => fn (?ContributorRole $role) : string => $role ? $role->label() : '', + 'help' => 'Select one or more roles from the list of MARC relator terms. You can see view the full descriptions of each role here.', + 'help_html' => true, 'attr' => [ - 'add_path' => 'contributor_role_new', - 'add_label' => 'Add Role', + 'class' => 'select2-simple', + 'data-theme' => 'bootstrap-5', ], - 'placeholder' => 'Search for an existing contributor role by name', + 'placeholder' => 'Select contributor roles', ]); } @@ -54,6 +66,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void public function configureOptions(OptionsResolver $resolver) : void { $resolver->setDefaults([ 'data_class' => Contribution::class, + 'podcast' => null, ]); } } diff --git a/src/Form/ContributorRoleType.php b/src/Form/ContributorRoleType.php deleted file mode 100644 index 5598b72..0000000 --- a/src/Form/ContributorRoleType.php +++ /dev/null @@ -1,41 +0,0 @@ -add('relatorTerm', TextType::class, [ - 'label' => 'MARC Relator Term', - 'required' => false, - 'help' => 'One of the three letter codes from this list', - 'help_html' => true, - ]); - parent::buildForm($builder, $options); - } - - /** - * Define options for the form. - * - * Set default, optional, and required options passed to the - * buildForm() method via the $options parameter. - */ - public function configureOptions(OptionsResolver $resolver) : void { - $resolver->setDefaults([ - 'data_class' => ContributorRole::class, - ]); - } -} diff --git a/src/Form/EpisodeType.php b/src/Form/EpisodeType.php index 6f5e814..9cf0c45 100644 --- a/src/Form/EpisodeType.php +++ b/src/Form/EpisodeType.php @@ -24,8 +24,7 @@ class EpisodeType extends AbstractType { public function __construct( public UrlGeneratorInterface $router, - ) { - } + ) {} /** * Add form fields to $builder. @@ -37,10 +36,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'remote_route' => 'season_typeahead', 'remote_params' => ['podcast_id' => $builder->getData()->getPodcast()->getId()], 'allow_clear' => true, - 'attr' => [ - 'add_route' => $this->router->generate('season_new', ['podcast_id' => $builder->getData()->getPodcast()->getId()]), - 'add_label' => 'Add Season', - ], 'placeholder' => 'Search for an existing season by title', 'required' => true, ]); @@ -106,13 +101,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'class' => 'tinymce', ], ]); - $builder->add('transcript', TextareaType::class, [ - 'label' => 'Transcript', - 'required' => false, - 'attr' => [ - 'class' => 'tinymce', - ], - ]); $builder->add('permissions', TextareaType::class, [ 'label' => 'Permissions', 'required' => false, @@ -132,21 +120,24 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void ], 'attr' => [ 'class' => 'collection collection-complex', + 'data-collection-label' => 'Keyword', ], ]); $builder->add('contributions', CollectionType::class, [ 'label' => 'Contributors', - 'required' => false, + 'required' => true, 'allow_add' => true, 'allow_delete' => true, 'delete_empty' => true, 'entry_type' => ContributionType::class, 'entry_options' => [ 'label' => false, + 'podcast' => $builder->getData()->getPodcast(), ], 'by_reference' => false, 'attr' => [ 'class' => 'collection collection-complex', + 'data-collection-label' => 'Contributor', ], ]); $builder->add('audios', CollectionType::class, [ @@ -167,7 +158,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void ]); $builder->add('images', CollectionType::class, [ 'label' => 'Images', - 'required' => false, + 'required' => true, 'allow_add' => true, 'allow_delete' => true, 'delete_empty' => true, @@ -181,9 +172,16 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'class' => 'collection collection-media collection-media-image', ], ]); - $builder->add('pdfs', CollectionType::class, [ - 'label' => 'Transcripts', + $builder->add('transcript', TextareaType::class, [ + 'label' => 'Transcript (Text)', 'required' => false, + 'attr' => [ + 'class' => 'tinymce', + ], + ]); + $builder->add('pdfs', CollectionType::class, [ + 'label' => 'Transcript (PDF)', + 'required' => true, 'allow_add' => true, 'allow_delete' => true, 'delete_empty' => true, diff --git a/src/Form/InstitutionType.php b/src/Form/InstitutionType.php deleted file mode 100644 index 28ede23..0000000 --- a/src/Form/InstitutionType.php +++ /dev/null @@ -1,42 +0,0 @@ -add('country', TextType::class, [ - 'label' => 'Country', - 'required' => true, - ]); - $builder->add('name', TextType::class, [ - 'label' => 'Name', - 'required' => true, - ]); - } - - /** - * Define options for the form. - * - * Set default, optional, and required options passed to the - * buildForm() method via the $options parameter. - */ - public function configureOptions(OptionsResolver $resolver) : void { - $resolver->setDefaults([ - 'data_class' => Institution::class, - ]); - } -} diff --git a/src/Form/PersonType.php b/src/Form/PersonType.php index 3970399..160c347 100644 --- a/src/Form/PersonType.php +++ b/src/Form/PersonType.php @@ -4,22 +4,234 @@ namespace App\Form; -use App\Entity\Institution; use App\Entity\Person; -use Nines\MediaBundle\Form\LinkableType; -use Nines\MediaBundle\Form\Mapper\LinkableMapper; +use Nines\UtilBundle\Form\DatalistType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Tetranz\Select2EntityBundle\Form\Type\Select2EntityType; /** * Person form. */ class PersonType extends AbstractType { - private ?LinkableMapper $mapper = null; + private static array $DEFAULT_INSTITUTIONS = [ + 'Acadia University', + 'Adler Graduate Professional School Inc.', + 'Adler University - Vancouver', + 'Alberta College of Art and Design', + 'Algoma University', + 'Algonquin College', + 'Ambrose University', + 'Assiniboine Community College', + 'Athabasca University', + 'Aurora College', + 'Banff Centre for Arts and Creativity', + "Bishop's University", + 'Bow Valley College', + 'Brandon University', + 'Brescia University College', + 'British Columbia Institute of Technology', + 'Brock University', + 'Burman University', + 'Cambrian College of Applied Arts and Technology', + 'Camosun College', + 'Campus Notre-Dame-de-Foy', + 'Canadian Mennonite University', + 'Canadore College', + 'Cape Breton University', + 'Capilano University', + 'Carleton University', + 'Cégep André Laurendeau', + 'Cégep de Drummondville', + 'Cégep de Granby', + 'Cégep de Jonquière', + "Cégep de l'Outaouais", + 'Cégep de la Gaspésie et des îles', + 'Cégep de La Pocatière', + 'Cégep de Rivière-du-Loup', + 'Cégep de Saint-Félicien', + 'Cégep de Saint-Hyacinthe', + 'Cégep de Saint-Laurent', + 'Cégep de Sainte-Foy', + 'Cégep de Sept-Îles', + 'Cégep de Sherbrooke', + 'Cégep de Sorel-Tracy', + 'Cégep de Thetford', + 'Cégep de Trois-Rivières', + 'Cégep de Victoriaville', + 'Cégep du Vieux Montréal', + 'Cégep Édouard-Montpetit', + 'Cégep Garneau', + 'Cégep Gérald-Godin', + 'Cégep Limoilou', + 'Cégep Marie-Victorin', + 'Cégep régional de Lanaudière', + 'Cégep Saint-Jean-sur-Richelieu', + 'Centennial College', + 'Champlain Regional College', + 'Coast Mountain College', + 'Collège Boréal', + 'Collège communautaire du Nouveau-Brunswick', + 'Collège de Bois-de-Boulogne', + 'Collège de Maisonneuve', + 'Collège de Rosemont', + 'Collège de Valleyfield', + 'Collège Laflèche', + 'Collège Lionel-Groulx', + 'Collège Mérici', + 'Collège Montmorency', + 'College of New Caledonia', + 'College of the North Atlantic', + 'College of the Rockies', + 'Concordia University', + 'Concordia University of Edmonton', + 'Conestoga College Institute of Technology and Advanced Learning', + 'Confederation College', + 'Crandall University', + 'Cumberland College', + 'Dalhousie University', + 'Dawson College', + 'Dominican University College', + 'Douglas College', + 'Durham College', + 'École de technologie supérieure', + "École nationale d'administration publique", + 'École nationale de police du Québec', + 'Emily Carr University of Art + Design', + 'Fanshawe College', + 'George Brown College', + 'Georgian College of Applied Arts and Technology', + 'Grande Prairie Regional College', + 'Grant MacEwan University', + 'HEC Montréal', + 'Holland College', + 'Humber College Institute of Technology and Advanced Learning', + 'Huron University College', + "Institut de tourisme et d'hôtellerie du Québec", + 'Institut national de la recherche scientifique', + 'Institute for Christian Studies', + 'John Abbott College', + 'Justice Institute of British Columbia', + "King's University College at Western University", + 'Kutenai Art Therapy Institute', + 'Kwantlen Polytechnic University', + 'La Cité collégiale', + 'Lakehead University', + 'Lakeland College', + 'Lambton College', + 'Langara College', + 'Laurentian University', + 'Lethbridge College', + 'Loyalist College of Applied Arts and Technology', + 'McGill University', + 'McMaster University', + 'Medicine Hat College', + 'Memorial University of Newfoundland', + 'Mohawk College of Applied Arts and Technology', + 'Mount Allison University', + 'Mount Royal University', + 'Mount Saint Vincent University', + 'NAIT', + 'National Circus School', + 'New Brunswick Community College', + 'Niagara College Canada', + 'Nipissing University', + 'NorQuest College', + 'North Island College', + 'Northern Alberta Institute of Technology', + 'Northern College of Applied Arts and Technology', + 'Northern Lights College', + 'Northwest Territories', + 'Nova Scotia College of Art and Design University', + 'Nova Scotia Community College', + 'Nunavut Arctic College', + 'OCAD University', + 'Okanagan College', + 'Olds College', + 'Ontario', + 'Parkland College', + 'Polytechnique Montréal', + 'Portage College', + 'Prince Edward Island', + 'Quebec', + "Queen's University", + 'Red Deer College', + 'Red River College', + 'Redeemer University College', + 'Regent College', + 'Royal Military College of Canada', + 'Royal Roads University', + 'Ryerson University', + "Saint Mary's University", + 'Saint Paul University', + 'Saskatchewan Polytechnic', + 'Selkirk College', + 'Seneca College for Applied Arts and Technology', + 'Sheridan College Institute of Technology and Advanced Learning', + 'Simon Fraser University', + 'Sir Sandford Fleming College of Applied Arts and Technology', + 'Southern Alberta Institute of Technology', + 'St. Clair College', + 'St. Francis Xavier University', + 'St. Lawrence College of Applied Arts and Technology', + "St. Mary's University - Calgary", + 'St. Thomas More College', + 'St. Thomas University', + 'Télé-université', + "The King's University (Edmonton)", + 'The University of British Columbia', + 'The University of Winnipeg', + 'Thompson Rivers University', + 'Trent University', + 'Trinity Western University', + 'Tyndale University College & Seminary', + 'Unity Health Toronto', + 'Université de Moncton', + 'Université de Montréal', + 'Université de Saint-Boniface', + 'Université de Sherbrooke', + 'Université du Québec à Chicoutimi', + 'Université du Québec à Montréal', + 'Université du Québec à Rimouski', + 'Université du Québec à Trois-Rivières', + 'Université du Québec en Abitibi-Témiscamingue', + 'Université du Québec en Outaouais', + 'Université Laval', + 'Université Sainte-Anne', + 'University Blue Quills', + 'University College London', + 'University College of the North', + 'University of Alberta', + 'University of Calgary', + 'University of Guelph', + "University of King's College (Halifax)", + 'University of Lethbridge', + 'University of Manitoba', + 'University of New Brunswick', + 'University of Northern British Columbia', + 'University of Ontario Institute of Technology', + 'University of Ottawa', + 'University of Prince Edward Island', + 'University of Regina', + 'University of Saskatchewan', + "University of St. Michael's College", + 'University of the Fraser Valley', + 'University of Toronto', + 'University of Victoria', + 'University of Waterloo', + 'University of Windsor', + 'Vancouver Community College', + 'Vancouver Island University', + 'Vanier College', + 'Victoria University, Toronto', + 'Western University', + 'Wilfrid Laurier University', + 'Wycliffe College', + 'York University', + 'Yukon College', + ]; /** * Add form fields to $builder. @@ -44,24 +256,11 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'class' => 'tinymce', ], ]); - $builder->add('institution', Select2EntityType::class, [ + $builder->add('institution', DatalistType::class, [ 'label' => 'Institution', - 'class' => Institution::class, - 'remote_route' => 'institution_typeahead', - 'allow_clear' => true, - 'attr' => [ - 'add_path' => 'institution_new', - 'add_label' => 'Add Institution', - ], - 'placeholder' => 'Search for an existing person by name', + 'required' => false, + 'datalist' => self::$DEFAULT_INSTITUTIONS, ]); - LinkableType::add($builder, $options); - $builder->setDataMapper($this->mapper); - } - - #[\Symfony\Contracts\Service\Attribute\Required] - public function setMapper(LinkableMapper $mapper) : void { - $this->mapper = $mapper; } /** diff --git a/src/Form/PodcastType.php b/src/Form/PodcastType.php index 3a7e2ee..db074be 100644 --- a/src/Form/PodcastType.php +++ b/src/Form/PodcastType.php @@ -24,8 +24,7 @@ class PodcastType extends AbstractType { public function __construct( public UrlGeneratorInterface $router, - ) { - } + ) {} /** * Add form fields to $builder. @@ -62,6 +61,10 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'expanded' => false, 'multiple' => false, 'preferred_choices' => ['en', 'fr'], + 'attr' => [ + 'class' => 'select2-simple', + 'data-theme' => 'bootstrap-5', + ], ]); $builder->add('description', TextareaType::class, [ 'label' => 'Description', @@ -91,26 +94,30 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'label' => 'Publisher', 'class' => Publisher::class, 'remote_route' => 'publisher_typeahead', + 'remote_params' => ['podcast_id' => $builder->getData()->getId()], 'allow_clear' => true, 'attr' => [ - 'add_path' => 'publisher_new', + 'add_route' => $this->router->generate('publisher_new', ['podcast_id' => $builder->getData()->getId()]), 'add_label' => 'Add Publisher', + 'add_modal' => true, ], 'placeholder' => 'Search for an existing publisher by name', ]); $builder->add('contributions', CollectionType::class, [ 'label' => 'Contributors', - 'required' => false, + 'required' => true, 'allow_add' => true, 'allow_delete' => true, 'delete_empty' => true, 'entry_type' => ContributionType::class, 'entry_options' => [ 'label' => false, + 'podcast' => $builder->getData(), ], 'by_reference' => false, 'attr' => [ 'class' => 'collection collection-complex', + 'data-collection-label' => 'Contributor', ], ]); $builder->add('categories', CollectionType::class, [ @@ -132,6 +139,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'by_reference' => false, 'attr' => [ 'class' => 'collection collection-complex', + 'data-collection-label' => 'Apple Podcast Category', ], ]); $builder->add('keywords', CollectionType::class, [ @@ -146,6 +154,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void ], 'attr' => [ 'class' => 'collection collection-complex', + 'data-collection-label' => 'Keyword', ], ]); $builder->add('images', CollectionType::class, [ diff --git a/src/Form/SeasonType.php b/src/Form/SeasonType.php index 544cc44..b3178db 100644 --- a/src/Form/SeasonType.php +++ b/src/Form/SeasonType.php @@ -21,8 +21,7 @@ class SeasonType extends AbstractType { public function __construct( public UrlGeneratorInterface $router, - ) { - } + ) {} /** * Add form fields to $builder. @@ -52,31 +51,36 @@ public function buildForm(FormBuilderInterface $builder, array $options) : void 'label' => 'Publisher', 'class' => Publisher::class, 'remote_route' => 'publisher_typeahead', + 'remote_params' => ['podcast_id' => $builder->getData()->getPodcast()->getId()], 'allow_clear' => true, 'attr' => [ - 'add_path' => 'publisher_new', + 'add_route' => $this->router->generate('publisher_new', ['podcast_id' => $builder->getData()->getPodcast()->getId()]), 'add_label' => 'Add Publisher', + 'add_modal' => true, ], 'placeholder' => 'Search for an existing publisher by name', ]); + $builder->add('contributions', CollectionType::class, [ 'label' => 'Contributors', - 'required' => false, + 'required' => true, 'allow_add' => true, 'allow_delete' => true, 'delete_empty' => true, 'entry_type' => ContributionType::class, 'entry_options' => [ 'label' => false, + 'podcast' => $builder->getData()->getPodcast(), ], 'by_reference' => false, 'attr' => [ 'class' => 'collection collection-complex', + 'data-collection-label' => 'Contributor', ], ]); $builder->add('images', CollectionType::class, [ 'label' => 'Images', - 'required' => false, + 'required' => true, 'allow_add' => true, 'allow_delete' => true, 'delete_empty' => false, diff --git a/src/Menu/Builder.php b/src/Menu/Builder.php index 257925f..896297b 100644 --- a/src/Menu/Builder.php +++ b/src/Menu/Builder.php @@ -26,8 +26,7 @@ public function __construct( private TokenStorageInterface $tokenStorage, private PodcastRepository $podcastRepository, private ParameterBagInterface $parameterBagInterface, - ) { - } + ) {} private function hasRole(string $role) : bool { if ( ! $this->tokenStorage->getToken()) { @@ -111,56 +110,6 @@ public function mainSidebarMenu(array $options) : ItemInterface { 'safe_label' => true, ], ]); - - $contributorsMenu = $menu->addChild('Contributors', [ - 'uri' => '#', - 'label' => 'Contributors', - 'attributes' => [ - 'class' => 'mb-1', - ], - 'linkAttributes' => [ - 'class' => 'btn btn-toggle d-inline-flex align-items-center rounded border-0 collapsed', - 'data-bs-toggle' => 'collapse', - 'data-bs-target' => '#contributors-collapse', - 'aria-expanded' => 'true', - ], - 'childrenAttributes' => [ - 'class' => 'collapse show btn-toggle-nav list-unstyled fw-normal pb-1 small', - 'id' => 'contributors-collapse', - ], - ]); - - $contributorsMenu->addChild('People', [ - 'route' => 'person_index', - 'linkAttributes' => [ - 'class' => 'link-dark d-inline-flex text-decoration-none rounded', - ], - ]); - $contributorsMenu->addChild('Contributor Roles', [ - 'route' => 'contributor_role_index', - 'linkAttributes' => [ - 'class' => 'link-dark d-inline-flex text-decoration-none rounded', - ], - ]); - $contributorsMenu->addChild('Publishers', [ - 'route' => 'publisher_index', - 'linkAttributes' => [ - 'class' => 'link-dark d-inline-flex text-decoration-none rounded', - ], - ]); - $contributorsMenu->addChild('Institutions', [ - 'route' => 'institution_index', - 'linkAttributes' => [ - 'class' => 'link-dark d-inline-flex text-decoration-none rounded', - ], - ]); - - $menu->addChild('divider3', [ - 'label' => '
', - 'extras' => [ - 'safe_label' => true, - ], - ]); } $menu->addChild('Documentation', [ diff --git a/src/Message/ExportCleanupMessage.php b/src/Message/ExportCleanupMessage.php index b57c58b..97f9d87 100644 --- a/src/Message/ExportCleanupMessage.php +++ b/src/Message/ExportCleanupMessage.php @@ -5,6 +5,5 @@ namespace App\Message; class ExportCleanupMessage { - public function __construct() { - } + public function __construct() {} } diff --git a/src/Message/ExportMessage.php b/src/Message/ExportMessage.php index 2bf016a..170df93 100644 --- a/src/Message/ExportMessage.php +++ b/src/Message/ExportMessage.php @@ -7,8 +7,7 @@ class ExportMessage { public function __construct( private int $exportId, - ) { - } + ) {} public function getExportId() : int { return $this->exportId; diff --git a/src/Message/ImportMessage.php b/src/Message/ImportMessage.php index 4b54cd2..d27a1b1 100644 --- a/src/Message/ImportMessage.php +++ b/src/Message/ImportMessage.php @@ -7,8 +7,7 @@ class ImportMessage { public function __construct( private int $importId, - ) { - } + ) {} public function getImportId() : int { return $this->importId; diff --git a/src/MessageHandler/ExportCleanupHandler.php b/src/MessageHandler/ExportCleanupHandler.php index 35d89a5..9cbea3f 100644 --- a/src/MessageHandler/ExportCleanupHandler.php +++ b/src/MessageHandler/ExportCleanupHandler.php @@ -18,8 +18,7 @@ class ExportCleanupHandler { public function __construct( private KernelInterface $kernel, private LoggerInterface $messengerLogger, - ) { - } + ) {} public function __invoke(ExportCleanupMessage $exportCleanupMessage) : void { try { diff --git a/src/MessageHandler/ExportHandler.php b/src/MessageHandler/ExportHandler.php index a11150e..157b525 100644 --- a/src/MessageHandler/ExportHandler.php +++ b/src/MessageHandler/ExportHandler.php @@ -22,8 +22,7 @@ public function __construct( private EntityManagerInterface $entityManager, private ExportRepository $exportRepository, private LoggerInterface $messengerLogger, - ) { - } + ) {} public function __invoke(ExportMessage $exportMessage) : void { $export = $this->exportRepository->find($exportMessage->getExportId()); diff --git a/src/MessageHandler/ImportHandler.php b/src/MessageHandler/ImportHandler.php index 89d62ab..c56ff2a 100644 --- a/src/MessageHandler/ImportHandler.php +++ b/src/MessageHandler/ImportHandler.php @@ -22,8 +22,7 @@ public function __construct( private EntityManagerInterface $entityManager, private ImportRepository $importRepository, private LoggerInterface $messengerLogger, - ) { - } + ) {} public function __invoke(ImportMessage $importMessage) : void { $import = $this->importRepository->find($importMessage->getImportId()); diff --git a/src/Repository/ContributorRoleRepository.php b/src/Repository/ContributorRoleRepository.php deleted file mode 100644 index 591d200..0000000 --- a/src/Repository/ContributorRoleRepository.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class InstitutionRepository extends ServiceEntityRepository { - public function __construct(ManagerRegistry $registry) { - parent::__construct($registry, Institution::class); - } - - public function indexQuery() : Query { - return $this->createQueryBuilder('institution') - ->orderBy('institution.name', 'ASC') - ->addOrderBy('institution.country', 'ASC') - ->getQuery() - ; - } - - public function typeaheadQuery(string $q) : Query { - return $this->createQueryBuilder('institution') - ->andWhere('institution.name LIKE :q') - ->orderBy('institution.name', 'ASC') - ->addOrderBy('institution.country', 'ASC') - ->setParameter('q', "%{$q}%") - ->getQuery() - ; - } - - public function searchQuery(string $q) : Query { - return $this->createQueryBuilder('institution') - ->addSelect('MATCH (institution.name) AGAINST(:q BOOLEAN) as HIDDEN score') - ->andHaving('score > 0') - ->orderBy('score', 'DESC') - ->addOrderBy('institution.name', 'ASC') - ->addOrderBy('institution.country', 'ASC') - ->setParameter('q', $q) - ->getQuery() - ; - } -} diff --git a/src/Repository/PersonRepository.php b/src/Repository/PersonRepository.php index 670255d..0aa927b 100644 --- a/src/Repository/PersonRepository.php +++ b/src/Repository/PersonRepository.php @@ -5,6 +5,7 @@ namespace App\Repository; use App\Entity\Person; +use App\Entity\Podcast; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\Query; use Doctrine\Persistence\ManagerRegistry; @@ -22,29 +23,35 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Person::class); } - public function indexQuery() : Query { + public function indexQuery(Podcast $podcast) : Query { return $this->createQueryBuilder('person') + ->andWhere('person.podcast = :p') ->orderBy('person.sortableName', 'ASC') + ->setParameter('p', $podcast->getId()) ->getQuery() ; } - public function typeaheadQuery(string $q) : Query { + public function typeaheadQuery(Podcast $podcast, string $q) : Query { return $this->createQueryBuilder('person') ->andWhere('person.fullname LIKE :q') + ->andWhere('person.podcast = :p') ->orderBy('person.sortableName', 'ASC') ->setParameter('q', "%{$q}%") + ->setParameter('p', $podcast->getId()) ->getQuery() ; } - public function searchQuery(string $q) : Query { + public function searchQuery(Podcast $podcast, string $q) : Query { return $this->createQueryBuilder('person') ->addSelect('MATCH (person.fullname, person.bio) AGAINST(:q BOOLEAN) as HIDDEN score') + ->andWhere('person.podcast = :p') ->andHaving('score > 0') ->orderBy('score', 'DESC') ->addOrderBy('person.sortableName', 'ASC') ->setParameter('q', $q) + ->setParameter('p', $podcast->getId()) ->getQuery() ; } diff --git a/src/Repository/PublisherRepository.php b/src/Repository/PublisherRepository.php index 7def6f3..c103675 100644 --- a/src/Repository/PublisherRepository.php +++ b/src/Repository/PublisherRepository.php @@ -4,6 +4,7 @@ namespace App\Repository; +use App\Entity\Podcast; use App\Entity\Publisher; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\Query; @@ -22,29 +23,35 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Publisher::class); } - public function indexQuery() : Query { + public function indexQuery(Podcast $podcast) : Query { return $this->createQueryBuilder('publisher') + ->andWhere('publisher.podcast = :p') ->orderBy('publisher.name', 'ASC') + ->setParameter('p', $podcast->getId()) ->getQuery() ; } - public function typeaheadQuery(string $q) : Query { + public function typeaheadQuery(Podcast $podcast, string $q) : Query { return $this->createQueryBuilder('publisher') + ->andWhere('publisher.podcast = :p') ->andWhere('publisher.name LIKE :q') ->orderBy('publisher.name', 'ASC') ->setParameter('q', "%{$q}%") + ->setParameter('p', $podcast->getId()) ->getQuery() ; } - public function searchQuery(string $q) : Query { + public function searchQuery(Podcast $podcast, string $q) : Query { return $this->createQueryBuilder('publisher') ->addSelect('MATCH (publisher.name, publisher.description) AGAINST(:q BOOLEAN) as HIDDEN score') + ->andWhere('publisher.podcast = :p') ->andHaving('score > 0') ->orderBy('score', 'DESC') ->addOrderBy('publisher.name', 'ASC') ->setParameter('q', $q) + ->setParameter('p', $podcast->getId()) ->getQuery() ; } diff --git a/src/Security/PodcastVoter.php b/src/Security/PodcastVoter.php index 20458d8..a822f0f 100644 --- a/src/Security/PodcastVoter.php +++ b/src/Security/PodcastVoter.php @@ -16,8 +16,7 @@ class PodcastVoter extends Voter { public function __construct( private Security $security, - ) { - } + ) {} private function canAccess(Podcast $podcast, User $user) : bool { foreach ($podcast->getShares() as $share) { diff --git a/src/Service/BepressExport.php b/src/Service/BepressExport.php index 95fb084..6633d0a 100644 --- a/src/Service/BepressExport.php +++ b/src/Service/BepressExport.php @@ -4,7 +4,7 @@ namespace App\Service; -use App\Entity\ContributorRole; +use App\Config\ContributorRole; use App\Entity\Episode; use App\Entity\Person; use Exception; @@ -15,20 +15,7 @@ class BepressExport extends ExportService { private function getAuthors(Episode $episode) : array { $contributions = $this->getEpisodeContributorPersonAndRoles($episode); - $priorityRoleFilter = function (ContributorRole $contributorRole) : bool { - foreach (['author', 'host'] as $roleName) { - if (str_contains(mb_strtolower($contributorRole->getLabel()), $roleName)) { - return true; - } - } - foreach (['aud', 'aut', 'hst'] as $relatorTerm) { - if (str_contains(mb_strtolower($contributorRole->getRelatorTerm()), $relatorTerm)) { - return true; - } - } - - return false; - }; + $priorityRoleFilter = fn (ContributorRole $contributorRole) : bool => in_array($contributorRole, [ContributorRole::aud, ContributorRole::aut, ContributorRole::hst], true); usort($contributions, function (array $left, array $right) use ($priorityRoleFilter) : int { $leftMatches = count(array_filter($left['roles'], $priorityRoleFilter)) > 0; @@ -190,19 +177,19 @@ protected function generate() : void { 'author1_fname' => $this->getFirstName($author1), // author1_lname `required` 'author1_lname' => $this->getLastName($author1), - 'author1_institution' => $author1?->getInstitution()?->getName(), + 'author1_institution' => $author1?->getInstitution(), 'author2_fname' => $this->getFirstName($author2), 'author2_lname' => $this->getLastName($author2), - 'author2_institution' => $author2?->getInstitution()?->getName(), + 'author2_institution' => $author2?->getInstitution(), 'author3_fname' => $this->getFirstName($author3), 'author3_lname' => $this->getLastName($author3), - 'author3_institution' => $author3?->getInstitution()?->getName(), + 'author3_institution' => $author3?->getInstitution(), 'author4_fname' => $this->getFirstName($author4), 'author4_lname' => $this->getLastName($author4), - 'author4_institution' => $author4?->getInstitution()?->getName(), + 'author4_institution' => $author4?->getInstitution(), 'comments' => $episode->getTranscript(), 'document_type' => 'audio', diff --git a/src/Service/ExportService.php b/src/Service/ExportService.php index e901759..03501db 100644 --- a/src/Service/ExportService.php +++ b/src/Service/ExportService.php @@ -33,8 +33,7 @@ public function __construct( protected int $totalEpisodes = 0, protected int $totalSteps = 1, protected int $stepsCompleted = 0, - ) { - } + ) {} protected function getContributorPersonAndRoles(array $allContributions) : array { $contributions = []; @@ -46,7 +45,7 @@ protected function getContributorPersonAndRoles(array $allContributions) : array 'roles' => [], ]; } - $contributions[$person->getId()]['roles'][] = $contribution->getContributorRole(); + $contributions[$person->getId()]['roles'] = array_merge($contribution->getRoles(), $contributions[$person->getId()]['roles']); } return $contributions; diff --git a/src/Service/IslandoraExport.php b/src/Service/IslandoraExport.php index 306e3ee..f2a056d 100644 --- a/src/Service/IslandoraExport.php +++ b/src/Service/IslandoraExport.php @@ -68,9 +68,7 @@ private function getLinkedAgents(array $contributions) : string { foreach ($contributions as $contribution) { $person = $contribution['person']; foreach ($contribution['roles'] as $role) { - if ($role->getRelatorTerm()) { - $linked_agents[] = "relators:{$role->getRelatorTerm()}:person:{$person->getSortableName()}"; - } + $linked_agents[] = "relators:{$role->value}:person:{$person->getSortableName()}"; } } diff --git a/templates/base.html.twig b/templates/base.html.twig index 56a5c1b..2506ad9 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -5,7 +5,7 @@ - {% block title %}Amplify Podcast Network{% endblock %} + {% block title %}AMP Podcast Network{% endblock %} @@ -35,7 +35,7 @@
  - Amplify @ SFU + AMP @ SFU {% if git_repo and (git_tag or git_commit) %} @@ -56,7 +56,7 @@
-
+
{% block action_buttons %} {% endblock %} diff --git a/templates/contributor_role/edit.html.twig b/templates/contributor_role/edit.html.twig deleted file mode 100644 index d91000d..0000000 --- a/templates/contributor_role/edit.html.twig +++ /dev/null @@ -1,19 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Edit Contributor Role {% endblock %} - -{% block breadcrumbs %} - - - - -{% endblock %} - -{% block body %} - {% embed 'contributor_role/partial/form.html.twig' %} - {% endembed %} -{% endblock %} - -{% block javascripts %} - {% include '@NinesEditor/editor/widget.html.twig' %} -{% endblock %} \ No newline at end of file diff --git a/templates/contributor_role/index.html.twig b/templates/contributor_role/index.html.twig deleted file mode 100644 index 582ab3b..0000000 --- a/templates/contributor_role/index.html.twig +++ /dev/null @@ -1,23 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Contributor Role List{% endblock %} - -{% block breadcrumbs %} - - -{% endblock %} - -{% block action_buttons %} - - New - -{% endblock %} - -{% block body %} - {% embed '@NinesUtil/term/partial/index.html.twig' with { - 'terms': contributor_roles, - 'path': 'contributor_role_show', - 'caption': contributor_roles.getTotalItemCount ~ ' ' ~ (q ? 'found' : 'total'), - } %} - {% endembed %} -{% endblock %} diff --git a/templates/contributor_role/new.html.twig b/templates/contributor_role/new.html.twig deleted file mode 100644 index 4c26886..0000000 --- a/templates/contributor_role/new.html.twig +++ /dev/null @@ -1,18 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}New Contributor Role {% endblock %} - -{% block breadcrumbs %} - - - -{% endblock %} - -{% block body %} - {% embed 'contributor_role/partial/form.html.twig' %} - {% endembed %} -{% endblock %} - -{% block javascripts %} - {% include '@NinesEditor/editor/widget.html.twig' %} -{% endblock %} diff --git a/templates/contributor_role/partial/form.html.twig b/templates/contributor_role/partial/form.html.twig deleted file mode 100644 index 34a1514..0000000 --- a/templates/contributor_role/partial/form.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{{ form_start(form) }} -{{ form_widget(form) }} -
-
-
- {% if contributor_role.id is null %} - - Cancel - {% else %} - - Cancel - {% endif %} -
-
-{{ form_end(form) }} - diff --git a/templates/contributor_role/show.html.twig b/templates/contributor_role/show.html.twig deleted file mode 100644 index 9843e27..0000000 --- a/templates/contributor_role/show.html.twig +++ /dev/null @@ -1,47 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Contributor Role {{ contributor_role.label }}{% endblock %} - -{% block breadcrumbs %} - - - -{% endblock %} - -{% block action_buttons %} - - Edit - -
- - - -
-{% endblock %} - -{% block body %} - {% embed '@NinesUtil/term/partial/show.html.twig' with { - 'term': contributor_role - } %} - {% block callback %} - - MARC Relator Code - {{ contributor_role.relatorTerm }} - - - Contributors - - {% if contributor_role.contributions|length > 0 %} -
    - {% for contribution in contributor_role.contributions %} -
  • - {{ contribution }} -
  • - {% endfor %} -
- {% endif %} - - - {% endblock %} - {% endembed %} -{% endblock %} diff --git a/templates/default/privacy.html.twig b/templates/default/privacy.html.twig index b57d964..163cb17 100644 --- a/templates/default/privacy.html.twig +++ b/templates/default/privacy.html.twig @@ -1,7 +1,7 @@ {% extends 'base.html.twig' %} {% block breadcrumbs %} - + {% endblock %} diff --git a/templates/episode/edit.html.twig b/templates/episode/edit.html.twig index 602eeb4..7e9bc1d 100644 --- a/templates/episode/edit.html.twig +++ b/templates/episode/edit.html.twig @@ -3,7 +3,6 @@ {% block title %}Edit Episode {{ episode.title }}{% endblock %} {% block breadcrumbs %} - {% if episode.season %} diff --git a/templates/episode/new.html.twig b/templates/episode/new.html.twig index 9dc06b2..926acdf 100644 --- a/templates/episode/new.html.twig +++ b/templates/episode/new.html.twig @@ -3,7 +3,6 @@ {% block title %}New Episode{% endblock %} {% block breadcrumbs %} - {% if episode.season %} diff --git a/templates/episode/partial/form.html.twig b/templates/episode/partial/form.html.twig index 45f1de5..3d82c0e 100644 --- a/templates/episode/partial/form.html.twig +++ b/templates/episode/partial/form.html.twig @@ -8,15 +8,20 @@
{% if episode.id is null %} + Cancel {% else %} - + + Cancel {% endif %}
{{ form_end(form, { table_of_contents_submit: { - label: episode.id is null ? 'Create' : 'Update', + label: episode.id is null ? 'Create' : 'Save', + }, + table_of_contents_submit_and_continue: { + label: 'Save & Continue', }, table_of_contents_cancel: { path: episode.id is null ? path('podcast_show', { 'id': episode.podcast.id }) : path('episode_show', { 'podcast_id': episode.podcast.id, 'id': episode.id }), diff --git a/templates/episode/partial/table.html.twig b/templates/episode/partial/table.html.twig index 71cb90d..10e6e57 100644 --- a/templates/episode/partial/table.html.twig +++ b/templates/episode/partial/table.html.twig @@ -1,13 +1,10 @@
- - + @@ -18,27 +15,28 @@ {% endfor %} +
- {{ episodes|length ~ ' total' }}. -
Episode TitleEpisode Exportable StatusEpisode Export Status Last Updated Action
{{ episode.slug }} {{ episode.title|u.truncate(50, '...') }} - {% embed 'status/partial/badge.html.twig' with { - 'edit_link': path('episode_edit', {'podcast_id': episode.podcast.id, 'id': episode.id }), - 'status': episode.status, - } %} - {% endembed %} + {% embed 'status/partial/report.html.twig' with { + 'entity': episode, + } %}{% endembed %} {{ episode.updated|date('Y-m-d') }}
- Edit + Edit Episode
- +
+ {{ episodes|length }} total. +
\ No newline at end of file diff --git a/templates/episode/show.html.twig b/templates/episode/show.html.twig index 7c7cca8..ed30fb9 100644 --- a/templates/episode/show.html.twig +++ b/templates/episode/show.html.twig @@ -3,7 +3,6 @@ {% block title %}Episode {{ episode.title }}{% endblock %} {% block breadcrumbs %} - {% if episode.season %} @@ -14,16 +13,27 @@ {% block action_buttons %} - Edit + Edit Episode
- +
{% endblock %} {% block body %} +

+ Season {{ episode.season.number }} + {% if episode.episodeType == 'bonus' %} + Bonus Episode + {% elseif episode.episodeType == 'trailer' %} + Trailer + {% else %} + Episode + {% endif %} + {{ episode.number }} +

@@ -35,9 +45,9 @@ {% endembed %} {% endfor %}
-

{{ episode.title }}

+

{{ episode.title }}

{% if episode.subTitle %}
{{ episode.subTitle }}
{% endif %} -
{{ episode.slug }}
+
{{ episode.slug }}
{{ episode.description|default('')|sanitize_html }}
@@ -62,12 +72,11 @@
Contributors
- {% for contributions in episode.getContributionsGroupedByPerson %} - {% set person = (contributions|first).person %} + {% for contribution in episode.contributions %}
- {{ person.fullName }} - - {% for contribution in contributions %} - {{ contribution.contributorRole.label }}{{ not loop.last ? ',' : '' }} + {{ contribution.person.fullName }} - + {% for role in contribution.roles %} + {{ role.label|default('N/A') }} {% endfor %}
{% endfor %} @@ -81,10 +90,9 @@ {{ episode.updated|date('Y-m-d') }}
-
Episode Exportable Status
- {% embed 'status/partial/badge.html.twig' with { - 'edit_link': path('episode_edit', {'podcast_id': episode.podcast.id, 'id': episode.id }), - 'status': episode.status, +
Episode Export Status
+ {% embed 'status/partial/report.html.twig' with { + 'entity': episode, } %} {% endembed %}
diff --git a/templates/export/format/mods/partial/contribution.xml.twig b/templates/export/format/mods/partial/contribution.xml.twig index 35becdc..dd8714f 100644 --- a/templates/export/format/mods/partial/contribution.xml.twig +++ b/templates/export/format/mods/partial/contribution.xml.twig @@ -2,14 +2,12 @@ {{ person.sortableName }} {{ person.fullName }} {% if person.institution %} - {{ person.institution}} + {{ person.institution }} {% endif %} {% for role in roles %} {{ role.label }} - {% if role.relatorTerm %} - {{ role.relatorTerm }} - {% endif %} + {{ role.value }} {% endfor %} {% if person.bio %} diff --git a/templates/export/format/mods/partial/identifier_season.xml.twig b/templates/export/format/mods/partial/identifier_season.xml.twig index c94c3ca..9f97959 100644 --- a/templates/export/format/mods/partial/identifier_season.xml.twig +++ b/templates/export/format/mods/partial/identifier_season.xml.twig @@ -1,5 +1,5 @@ {{ path('season_show', {'podcast_id': season.podcast.id, 'id': season.id}) }} -urn:amplify:podcast:{{ season.podcast.id }}:season:{{ season.id }} +urn:amp:podcast:{{ season.podcast.id }}:season:{{ season.id }} {{ season.podcast.title }} diff --git a/templates/export/index.html.twig b/templates/export/index.html.twig index 1827af7..3228eef 100644 --- a/templates/export/index.html.twig +++ b/templates/export/index.html.twig @@ -3,7 +3,6 @@ {% block title %}Podcast {{ podcast.title }} Exports{% endblock %} {% block breadcrumbs %} - @@ -26,9 +25,6 @@
- @@ -72,6 +68,9 @@ {% endfor %} +
- {{ exports.getTotalItemCount ~ ' ' ~ (q ? 'found' : 'total') }}. -
Status
+ {{ exports.getTotalItemCount ~ ' ' ~ (q ? 'found' : 'total') }}. +
{{ knp_pagination_render(exports) }} diff --git a/templates/export/new.html.twig b/templates/export/new.html.twig index d9f1aef..82a0900 100644 --- a/templates/export/new.html.twig +++ b/templates/export/new.html.twig @@ -3,7 +3,6 @@ {% block title %}New Podcast Export{% endblock %} {% block breadcrumbs %} - @@ -13,12 +12,9 @@ {% block body %} {{ form_start(form) }} {{ form_widget(form) }} -
-
-
- - Cancel -
+ {{ form_end(form) }} {% endblock %} diff --git a/templates/export/show.html.twig b/templates/export/show.html.twig index e71f424..bdec05b 100644 --- a/templates/export/show.html.twig +++ b/templates/export/show.html.twig @@ -3,7 +3,6 @@ {% block title %}{{ export.format }} Export {{ export.id }} Status{% endblock %} {% block breadcrumbs %} - diff --git a/templates/import/new.html.twig b/templates/import/new.html.twig index 203718e..463a7f6 100644 --- a/templates/import/new.html.twig +++ b/templates/import/new.html.twig @@ -3,7 +3,6 @@ {% block title %}New Import{% endblock %} {% block breadcrumbs %} - @@ -12,12 +11,9 @@ {% block body %} {{ form_start(form) }} {{ form_widget(form) }} -
-
-
- - Cancel -
+ {{ form_end(form) }} {% endblock %} diff --git a/templates/import/show.html.twig b/templates/import/show.html.twig index 335e011..7f16f81 100644 --- a/templates/import/show.html.twig +++ b/templates/import/show.html.twig @@ -3,7 +3,6 @@ {% block title %}RSS Import {{ import.id }} Status{% endblock %} {% block breadcrumbs %} - {% if import.podcast %} diff --git a/templates/institution/edit.html.twig b/templates/institution/edit.html.twig deleted file mode 100644 index d637292..0000000 --- a/templates/institution/edit.html.twig +++ /dev/null @@ -1,19 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Edit Institution {{ institution.name }}{% endblock %} - -{% block breadcrumbs %} - - - - -{% endblock %} - -{% block body %} - {% embed 'institution/partial/form.html.twig' %} - {% endembed %} -{% endblock %} - -{% block javascripts %} - {% include '@NinesEditor/editor/widget.html.twig' %} -{% endblock %} \ No newline at end of file diff --git a/templates/institution/index.html.twig b/templates/institution/index.html.twig deleted file mode 100644 index 356d34e..0000000 --- a/templates/institution/index.html.twig +++ /dev/null @@ -1,56 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Institution List{% endblock %} - -{% block breadcrumbs %} - - -{% endblock %} - -{% block action_buttons %} - - New - -{% endblock %} - -{% block body %} -
-
- - - -
-
-
- - - - - - - - - - {% for institution in institutions %} - - - - - {% endfor %} - -
- {{ institutions.getTotalItemCount ~ ' ' ~ (q ? 'found' : 'total') }}. -
NameCountry
- - {{ institution.name }} - - - {{ institution.country }} -
-
- {{ knp_pagination_render(institutions) }} -{% endblock %} diff --git a/templates/institution/new.html.twig b/templates/institution/new.html.twig deleted file mode 100644 index aceabd1..0000000 --- a/templates/institution/new.html.twig +++ /dev/null @@ -1,18 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}New Institution{% endblock %} - -{% block breadcrumbs %} - - - -{% endblock %} - -{% block body %} - {% embed 'institution/partial/form.html.twig' %} - {% endembed %} -{% endblock %} - -{% block javascripts %} - {% include '@NinesEditor/editor/widget.html.twig' %} -{% endblock %} \ No newline at end of file diff --git a/templates/institution/partial/form.html.twig b/templates/institution/partial/form.html.twig deleted file mode 100644 index 8260ead..0000000 --- a/templates/institution/partial/form.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{{ form_start(form) }} -{{ form_widget(form) }} -
-
-
- {% if institution.id is null %} - - Cancel - {% else %} - - Cancel - {% endif %} -
-
-{{ form_end(form) }} - diff --git a/templates/institution/show.html.twig b/templates/institution/show.html.twig deleted file mode 100644 index e8307f4..0000000 --- a/templates/institution/show.html.twig +++ /dev/null @@ -1,66 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Institution {{ institution.name }}{% endblock %} - -{% block breadcrumbs %} - - - -{% endblock %} - -{% block action_buttons %} - - Edit - -
- - - -
-{% endblock %} - -{% block body %} -
- - - - - - - - - - - - - - - - - - - - - - - - -
Country - {{ institution.country }} -
Name - {{ institution.name }} -
Person - {% if institution.people|length > 0 %} - - {% endif %} -
Created{{ institution.created|date }}
Updated{{ institution.updated|date }}
-
-{% endblock %} diff --git a/templates/person/edit.html.twig b/templates/person/edit.html.twig index 2fcada8..2892742 100644 --- a/templates/person/edit.html.twig +++ b/templates/person/edit.html.twig @@ -3,15 +3,21 @@ {% block title %}Edit Person{% endblock %} {% block breadcrumbs %} - - - + + + + {% endblock %} {% block body %} - {% embed 'person/partial/form.html.twig' %} - {% endembed %} + {{ form_start(form) }} + {{ form_widget(form) }} +
+ + Cancel +
+ {{ form_end(form) }} {% endblock %} {% block javascripts %} diff --git a/templates/person/index.html.twig b/templates/person/index.html.twig index f60b2a3..6910f13 100644 --- a/templates/person/index.html.twig +++ b/templates/person/index.html.twig @@ -3,17 +3,13 @@ {% block title %}Person List{% endblock %} {% block breadcrumbs %} - + + {% endblock %} -{% block action_buttons %} - - New - -{% endblock %} - {% block body %} +

All Podcast Contributors

+
+ \ No newline at end of file diff --git a/templates/person/partial/form.html.twig b/templates/person/partial/form.html.twig deleted file mode 100644 index edddf89..0000000 --- a/templates/person/partial/form.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{{ form_start(form) }} -{{ form_widget(form) }} -
-
-
- {% if person.id is null %} - - Cancel - {% else %} - - Cancel - {% endif %} -
-
-{{ form_end(form) }} - diff --git a/templates/person/show.html.twig b/templates/person/show.html.twig deleted file mode 100644 index 17c31ee..0000000 --- a/templates/person/show.html.twig +++ /dev/null @@ -1,91 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Person Details {% endblock %} - -{% block breadcrumbs %} - - - -{% endblock %} - -{% block action_buttons %} - - Edit - -
- - - -
-{% endblock %} - -{% block body %} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Fullname - {{ person.fullname }} -
Sortable Name - {{ person.sortableName }} -
Location - {{ person.location }} -
Bio -
{{ person.bio|default('')|sanitize_html }}
-
Links - {% embed '@NinesMedia/link/partial/list.html.twig' with { - 'entity': person } %} - {% endembed %} -
Institution - {% if person.institution %} - {{ person.institution }} - {% endif %} -
Contributors - {% if person.contributions|length > 0 %} -
    - {% for contribution in person.contributions %} -
  • - {{ contribution }} -
  • - {% endfor %} -
- {% endif %} -
Created{{ person.created|date }}
Updated{{ person.updated|date }}
-
-{% endblock %} diff --git a/templates/podcast/edit.html.twig b/templates/podcast/edit.html.twig index 4eaeb01..6c79d5d 100644 --- a/templates/podcast/edit.html.twig +++ b/templates/podcast/edit.html.twig @@ -3,7 +3,7 @@ {% block title %}Edit Podcast {{ podcast.title }}{% endblock %} {% block breadcrumbs %} - + diff --git a/templates/podcast/index.html.twig b/templates/podcast/index.html.twig index bbf4528..a1a096a 100644 --- a/templates/podcast/index.html.twig +++ b/templates/podcast/index.html.twig @@ -3,16 +3,12 @@ {% block title %}Podcast List{% endblock %} {% block breadcrumbs %} - {% endblock %} {% block action_buttons %} - RSS Import - - - New + Import New Podcast from RSS Feed {% endblock %} @@ -30,13 +26,13 @@
- + + + @@ -47,14 +43,16 @@ {{ podcast.title }} - + + + + {% endfor %} +
- {{ podcasts.getTotalItemCount ~ ' ' ~ (q ? 'found' : 'total') }}. -
TitleTotal SeasonsTotal Episodes WebsiteRSS Feed
- {% if podcast.website %} - {{ podcast.website }} - {% endif %} - {{ podcast.seasons|length }}{{ podcast.episodes|length }}{{ podcast.website }}{{ podcast.rss }}
+ {{ podcasts.getTotalItemCount ~ ' ' ~ (q ? 'found' : 'total') }}. +
{{ knp_pagination_render(podcasts) }} diff --git a/templates/podcast/new.html.twig b/templates/podcast/new.html.twig index 154974a..1b0f97d 100644 --- a/templates/podcast/new.html.twig +++ b/templates/podcast/new.html.twig @@ -3,7 +3,6 @@ {% block title %}New Podcast{% endblock %} {% block breadcrumbs %} - {% endblock %} diff --git a/templates/podcast/partial/form.html.twig b/templates/podcast/partial/form.html.twig index 9a82acf..6f54a73 100644 --- a/templates/podcast/partial/form.html.twig +++ b/templates/podcast/partial/form.html.twig @@ -8,16 +8,21 @@
{% if podcast.id is null %} + Cancel {% else %} - + + Cancel {% endif %}
{{ form_end(form, { render_rest: true, table_of_contents_submit: { - label: podcast.id is null ? 'Create' : 'Update', + label: podcast.id is null ? 'Create' : 'Save', + }, + table_of_contents_submit_and_continue: { + label: 'Save & Continue', }, table_of_contents_cancel: { path: podcast.id is null ? path('podcast_index') : path('podcast_show', { 'id': podcast.id }), diff --git a/templates/podcast/show.html.twig b/templates/podcast/show.html.twig index de86a4e..7bbb2bd 100644 --- a/templates/podcast/show.html.twig +++ b/templates/podcast/show.html.twig @@ -3,28 +3,24 @@ {% block title %}Podcast {{ podcast.title }}{% endblock %} {% block breadcrumbs %} - {% endblock %} {% block action_buttons %} - - Share - - Export + Export Podcast - RSS Import + Reimport Metadata from RSS Feed - Edit + Edit Podcast
- +
{% endblock %} @@ -40,11 +36,23 @@ {% endembed %} {% endfor %}
-

{{ podcast.title }}

+

{{ podcast.title }}

{% if podcast.subTitle %}
{{ podcast.subTitle }}
{% endif %}
{{ podcast.description|default('')|sanitize_html }}
+ +
Explicit
@@ -69,7 +77,7 @@
Publisher
{% if podcast.publisher %} - {{ podcast.publisher }} + {{ podcast.publisher }} {% endif %}
@@ -86,21 +94,19 @@
Contributors
- {% for contributions in podcast.getContributionsGroupedByPerson %} - {% set person = (contributions|first).person %} + {% for contribution in podcast.contributions %}
- {{ person.fullName }} - - {% for contribution in contributions %} - {{ contribution.contributorRole.label }}{{ not loop.last ? ',' : '' }} + {{ contribution.person.fullName }} - + {% for role in contribution.roles %} + {{ role.label|default('N/A') }} {% endfor %}
{% endfor %}
-
Podcast Exportable Status
- {% embed 'status/partial/badge.html.twig' with { - 'edit_link': path('podcast_edit', {'id': podcast.id }), - 'status': podcast.status, +
Podcast Export Status
+ {% embed 'status/partial/report.html.twig' with { + 'entity': podcast, } %} {% endembed %}
@@ -128,6 +134,9 @@
+ + Edit Podcast + New Season @@ -138,18 +147,13 @@
- diff --git a/templates/publisher/edit.html.twig b/templates/publisher/edit.html.twig index 236ee86..856dfb0 100644 --- a/templates/publisher/edit.html.twig +++ b/templates/publisher/edit.html.twig @@ -3,15 +3,21 @@ {% block title %}Edit Publisher {{ publisher.name }}{% endblock %} {% block breadcrumbs %} - - - + + + + {% endblock %} {% block body %} - {% embed 'publisher/partial/form.html.twig' %} - {% endembed %} + {{ form_start(form) }} + {{ form_widget(form) }} +
+ + Cancel +
+ {{ form_end(form) }} {% endblock %} {% block javascripts %} diff --git a/templates/publisher/index.html.twig b/templates/publisher/index.html.twig index f7886d7..cf044ee 100644 --- a/templates/publisher/index.html.twig +++ b/templates/publisher/index.html.twig @@ -3,17 +3,13 @@ {% block title %}Publisher List{% endblock %} {% block breadcrumbs %} - + + {% endblock %} -{% block action_buttons %} - - New - -{% endblock %} - {% block body %} +

All Podcast Publishers

+
+ \ No newline at end of file diff --git a/templates/publisher/partial/form.html.twig b/templates/publisher/partial/form.html.twig deleted file mode 100644 index 14de024..0000000 --- a/templates/publisher/partial/form.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{{ form_start(form) }} -{{ form_widget(form) }} -
-
-
- {% if publisher.id is null %} - - Cancel - {% else %} - - Cancel - {% endif %} -
-
-{{ form_end(form) }} - diff --git a/templates/publisher/show.html.twig b/templates/publisher/show.html.twig deleted file mode 100644 index b1e203d..0000000 --- a/templates/publisher/show.html.twig +++ /dev/null @@ -1,94 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Publisher {{ publisher.name }}{% endblock %} - -{% block breadcrumbs %} - - - -{% endblock %} - -{% block action_buttons %} - - Edit - -
- - - -
-{% endblock %} - -{% block body %} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name{{ publisher.name }}
Location{{ publisher.location }}
Website{{ publisher.website }}
Description -
{{ publisher.description|default('')|sanitize_html }}
-
Contact -
{{ publisher.contact|default('')|sanitize_html }}
-
Podcast - {% if publisher.podcasts|length > 0 %} - - {% endif %} -
Season - {% if publisher.seasons|length > 0 %} - - {% endif %} -
Created{{ publisher.created|date }}
Updated{{ publisher.updated|date }}
-
-{% endblock %} diff --git a/templates/season/edit.html.twig b/templates/season/edit.html.twig index 9cb4558..375b833 100644 --- a/templates/season/edit.html.twig +++ b/templates/season/edit.html.twig @@ -3,7 +3,6 @@ {% block title %}Edit Season{% endblock %} {% block breadcrumbs %} - diff --git a/templates/season/new.html.twig b/templates/season/new.html.twig index d6854cc..d7e6c03 100644 --- a/templates/season/new.html.twig +++ b/templates/season/new.html.twig @@ -3,7 +3,6 @@ {% block title %}New Season{% endblock %} {% block breadcrumbs %} - diff --git a/templates/season/partial/form.html.twig b/templates/season/partial/form.html.twig index 0456eba..e887f7e 100644 --- a/templates/season/partial/form.html.twig +++ b/templates/season/partial/form.html.twig @@ -8,15 +8,20 @@
{% if season.id is null %} + Cancel {% else %} - + + Cancel {% endif %}
{{ form_end(form, { table_of_contents_submit: { - label: season.id is null ? 'Create' : 'Update', + label: season.id is null ? 'Create' : 'Save', + }, + table_of_contents_submit_and_continue: { + label: 'Save & Continue', }, table_of_contents_cancel: { path: season.id is null ? path('podcast_show', { 'id': season.podcast.id }) : path('season_show', { 'podcast_id': season.podcast.id, 'id': season.id }), diff --git a/templates/season/show.html.twig b/templates/season/show.html.twig index 5f3c511..95d7f27 100644 --- a/templates/season/show.html.twig +++ b/templates/season/show.html.twig @@ -3,7 +3,6 @@ {% block title %}Season {{ season.title }}{% endblock %} {% block breadcrumbs %} - @@ -11,17 +10,18 @@ {% block action_buttons %} - Edit + Edit Season
- +
{% endblock %} {% block body %} +

Season {{ season.number }}

@@ -34,7 +34,7 @@ {% endembed %} {% endfor %}
-

{{ season.title }}

+

{{ season.title }}

{% if season.subTitle %}
{{ season.subTitle }}
{% endif %} {% if season.number %}
Season {{ season.number }}
{% endif %}
{{ season.description|default('')|sanitize_html }}
@@ -44,26 +44,24 @@
Publisher
{% if season.publisher %} - {{ season.publisher }} + {{ season.publisher }} {% endif %}
Contributors
- {% for contributions in season.getContributionsGroupedByPerson %} - {% set person = (contributions|first).person %} + {% for contribution in season.contributions %}
- {{ person.fullName }} - - {% for contribution in contributions %} - {{ contribution.contributorRole.label }}{{ not loop.last ? ',' : '' }} + {{ contribution.person.fullName }} - + {% for role in contribution.roles %} + {{ role.label|default('N/A') }} {% endfor %}
{% endfor %}
-
Season Exportable Status
- {% embed 'status/partial/badge.html.twig' with { - 'edit_link': path('season_edit', {'podcast_id': season.podcast.id, 'id': season.id }), - 'status': season.status, +
Season Export Status
+ {% embed 'status/partial/report.html.twig' with { + 'entity': season, } %} {% endembed %}
diff --git a/templates/share/index.html.twig b/templates/share/index.html.twig index 569e8de..f4f4a86 100644 --- a/templates/share/index.html.twig +++ b/templates/share/index.html.twig @@ -3,19 +3,15 @@ {% block title %}Sharing to {{ podcast.title }}{% endblock %} {% block breadcrumbs %} - - + {% endblock %} {% block body %}

Users with access

- @@ -44,6 +40,9 @@ {% endfor %} +
- {{ shares|length ~ ' total' }}. -
Fullname
+ {{ shares|length ~ ' total' }}. +
diff --git a/templates/status/partial/badge.html.twig b/templates/status/partial/badge.html.twig deleted file mode 100644 index 987431a..0000000 --- a/templates/status/partial/badge.html.twig +++ /dev/null @@ -1,39 +0,0 @@ - diff --git a/templates/status/partial/episode.html.twig b/templates/status/partial/episode.html.twig new file mode 100644 index 0000000..c6ccc25 --- /dev/null +++ b/templates/status/partial/episode.html.twig @@ -0,0 +1,13 @@ +{% set statusItems = episode.status %} + +
    +{% embed 'status/partial/heading.html.twig' with { + 'label': episode.slug ~ ' Metadata: ' ~ episode.title, + 'count': statusItems|length +} %}{% endembed %} +
+
    +{% embed 'status/partial/status_items.html.twig' with { + 'items': statusItems +} %}{% endembed %} +
\ No newline at end of file diff --git a/templates/status/partial/heading.html.twig b/templates/status/partial/heading.html.twig new file mode 100644 index 0000000..cdac52d --- /dev/null +++ b/templates/status/partial/heading.html.twig @@ -0,0 +1,11 @@ + +
  • +
    +
    {{ label }}
    +
    + {% if count == 0 %} + + {% else %} + + {% endif %} +
  • diff --git a/templates/status/partial/podcast.html.twig b/templates/status/partial/podcast.html.twig new file mode 100644 index 0000000..b4dc808 --- /dev/null +++ b/templates/status/partial/podcast.html.twig @@ -0,0 +1,19 @@ +{% set statusItems = podcast.status|filter(v => not v.child is defined) %} + +
      +{% embed 'status/partial/heading.html.twig' with { + 'label': 'Podcast Metadata: ' ~ podcast.title, + 'count': statusItems|length +} %}{% endembed %} +
    +
      +{% embed 'status/partial/status_items.html.twig' with { + 'items': statusItems +} %}{% endembed %} +
    + +{% for season in podcast.seasons %} + {% embed 'status/partial/season.html.twig' with { + 'season': season + } %}{% endembed %} +{% endfor %} \ No newline at end of file diff --git a/templates/status/partial/report.html.twig b/templates/status/partial/report.html.twig new file mode 100644 index 0000000..4416703 --- /dev/null +++ b/templates/status/partial/report.html.twig @@ -0,0 +1,50 @@ +{% set statusCount = 0 %} +{% set id = 'temp_' %} +{% if constant('class', entity) is same as('App\\Entity\\Podcast') %} + {% set statusCount = podcast.status|length %} + {% set id = 'podcast_' ~ podcast.id %} +{% elseif constant('class', entity) is same as('App\\Entity\\Season') %} + {% set statusCount = season.status|length %} + {% set id = 'season_' ~ season.id %} +{% elseif constant('class', entity) is same as('App\\Entity\\Episode') %} + {% set statusCount = episode.status|length %} + {% set id = 'episode_' ~ episode.id %} +{% endif %} + +{% if statusCount == 0 %} + Ready for Export +{% else %} + +{% endif %} + + diff --git a/templates/status/partial/season.html.twig b/templates/status/partial/season.html.twig new file mode 100644 index 0000000..16ec0f9 --- /dev/null +++ b/templates/status/partial/season.html.twig @@ -0,0 +1,18 @@ +{% set statusItems = season.status|filter(v => not v.child is defined) %} + +
      +{% embed 'status/partial/heading.html.twig' with { + 'label': season.slug ~ ' Metadata: ' ~ season.title, + 'count': statusItems|length +} %}{% endembed %} +
    +
      +{% embed 'status/partial/status_items.html.twig' with { + 'items': statusItems +} %}{% endembed %} +
    +{% for episode in season.episodes %} + {% embed 'status/partial/episode.html.twig' with { + 'episode': episode + } %}{% endembed %} +{% endfor %} \ No newline at end of file diff --git a/templates/status/partial/status_items.html.twig b/templates/status/partial/status_items.html.twig new file mode 100644 index 0000000..fe40c27 --- /dev/null +++ b/templates/status/partial/status_items.html.twig @@ -0,0 +1,10 @@ +{% for item in items %} +
  • +
    + {{ item.label }} +
    + + Edit + +
  • +{% endfor %} \ No newline at end of file diff --git a/tests/Command/ImportPodcastCommandTest.php b/tests/Command/ImportPodcastCommandTest.php index 96ed57a..d1d3a75 100644 --- a/tests/Command/ImportPodcastCommandTest.php +++ b/tests/Command/ImportPodcastCommandTest.php @@ -231,7 +231,7 @@ public function testExecuteExistingPodcast() : void { 'episodes' => [ [ 'guid' => 'https://podcast.com/?p=1', - 'number' => 1, + 'number' => 1.0, 'date' => '2022-10-12T14:43:07-07:00', 'runTime' => '01:10:10', 'title' => 'episode 1 title stub', @@ -260,7 +260,7 @@ public function testExecuteExistingPodcast() : void { ], [ 'guid' => 'https://podcast.com/?p=2', - 'number' => 2, + 'number' => 2.0, 'date' => '2022-10-13T14:43:07-07:00', 'runTime' => '01:10:09', 'title' => 'episode 2 title stub', @@ -298,7 +298,7 @@ public function testExecuteExistingPodcast() : void { ]; // init state - $this->assertSame($this->podcastStub($podcast), [ + $this->assertEquals($this->podcastStub($podcast), [ 'title' => 'Title 3', 'subTitle' => 'SubTitle 3', 'explicit' => false, @@ -332,7 +332,7 @@ public function testExecuteExistingPodcast() : void { 'podcastId' => 4, ]); $podcast = $podcastRepository->find(4); - $this->assertSame($this->podcastStub($podcast), $expectedPodcast); + $this->assertEquals($this->podcastStub($podcast), $expectedPodcast); // running the import again will not add duplicate resources $mock->reset(); @@ -351,7 +351,7 @@ public function testExecuteExistingPodcast() : void { 'podcastId' => 4, ]); $podcast = $podcastRepository->find(4); - $this->assertSame($this->podcastStub($podcast), $expectedPodcast); + $this->assertEquals($this->podcastStub($podcast), $expectedPodcast); } protected function setUp() : void { diff --git a/tests/Controller/ContributorRoleTest.php b/tests/Controller/ContributorRoleTest.php deleted file mode 100644 index 2927a71..0000000 --- a/tests/Controller/ContributorRoleTest.php +++ /dev/null @@ -1,149 +0,0 @@ -client->request('GET', '/contributor_roles'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/contributor_roles'); - $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('New')->count()); - } - } - - public function testShow() : void { - // Anon - $crawler = $this->client->request('GET', '/contributor_roles/1'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/contributor_roles/1'); - $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); - } - } - - public function testTypeahead() : void { - // Anon - $this->client->request('GET', '/contributor_roles/typeahead?q=' . self::SEARCH_QUERY); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $this->client->request('GET', '/contributor_roles/typeahead?q=' . self::SEARCH_QUERY); - $response = $this->client->getResponse(); - $this->assertResponseIsSuccessful(); - $this->assertSame('application/json', $response->headers->get('content-type')); - $json = json_decode($response->getContent()); - $this->assertCount(4, $json); - } - } - - public function testSearch() : void { - // Anon - $crawler = $this->client->request('GET', '/contributor_roles'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/contributor_roles'); - $this->assertResponseIsSuccessful(); - - $form = $crawler->selectButton('btn-search')->form([ - 'q' => 'contributorRole', - ]); - - $responseCrawler = $this->client->submit($form); - $this->assertSame(200, $this->client->getResponse()->getStatusCode()); - } - } - - public function testEdit() : void { - // Anon - $crawler = $this->client->request('GET', '/contributor_roles/1/edit'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/contributor_roles/1/edit'); - $this->assertResponseIsSuccessful(); - - $form = $formCrawler->selectButton('Update')->form([ - 'contributor_role[label]' => 'Updated Label', - 'contributor_role[description]' => '

    Updated Text

    ', - 'contributor_role[relatorTerm]' => 'abc', - ]); - - $this->client->submit($form); - $this->assertResponseRedirects('/contributor_roles/1', Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - } - } - - public function testNew() : void { - // Anon - $crawler = $this->client->request('GET', '/contributor_roles/new'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - $newId = 5; - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/contributor_roles/new'); - $this->assertResponseIsSuccessful(); - - $form = $formCrawler->selectButton('Create')->form([ - 'contributor_role[label]' => "Updated Label{$newId}", - 'contributor_role[description]' => "

    Updated Text{$newId}

    ", - 'contributor_role[relatorTerm]' => "abc{$newId}", - ]); - - $this->client->submit($form); - $this->assertResponseRedirects("/contributor_roles/{$newId}", Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - $newId++; - } - } - - public function testDelete() : void { - $repo = self::getContainer()->get(ContributorRoleRepository::class); - $preCount = count($repo->findAll()); - - $this->login(UserFixtures::USER); - $crawler = $this->client->request('GET', '/contributor_roles/1'); - $this->assertResponseIsSuccessful(); - $form = $crawler->selectButton('Delete')->form(); - $this->client->submit($form); - - $this->assertResponseRedirects('/contributor_roles', Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - - $this->em->clear(); - $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); - } -} diff --git a/tests/Controller/EpisodeTest.php b/tests/Controller/EpisodeTest.php index 5dc10fa..dc8d140 100644 --- a/tests/Controller/EpisodeTest.php +++ b/tests/Controller/EpisodeTest.php @@ -28,7 +28,7 @@ public function testShow() : void { $this->login($loginCredentials); $crawler = $this->client->request('GET', '/podcasts/2/episodes/1'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); + $this->assertEquals(1, $crawler->filter('.page-actions')->selectLink('Edit Episode')->count()); } } @@ -48,8 +48,8 @@ public function testEdit() : void { $formCrawler = $this->client->request('GET', '/podcasts/2/episodes/1/edit'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Update')->form([ - 'episode[number]' => 10, + $form = $formCrawler->selectButton('Save')->form([ + 'episode[number]' => 10.5, 'episode[date]' => '2020-01-01', 'episode[runTime]' => '00:09:20', 'episode[title]' => 'Updated Title', @@ -122,6 +122,6 @@ public function testDelete() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); } } diff --git a/tests/Controller/ExportTest.php b/tests/Controller/ExportTest.php index b55d110..fb9fa84 100644 --- a/tests/Controller/ExportTest.php +++ b/tests/Controller/ExportTest.php @@ -31,7 +31,7 @@ public function testIndex() : void { $this->login($loginCredentials); $crawler = $this->client->request('GET', '/podcasts/2/exports'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('New')->count()); + $this->assertEquals(1, $crawler->filter('.page-actions')->selectLink('New')->count()); } } @@ -69,12 +69,12 @@ public function testNew() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount + 2, $postCount); + $this->assertEquals($preCount + 2, $postCount); $this->messenger('async')->queue()->assertCount(2); $this->messenger('async')->queue()->assertContains(ExportMessage::class, 2); - $this->assertSame($this->messenger('async')->queue()->messages(ExportMessage::class)[0]->getExportId(), 9); - $this->assertSame($this->messenger('async')->queue()->messages(ExportMessage::class)[1]->getExportId(), 10); + $this->assertEquals($this->messenger('async')->queue()->messages(ExportMessage::class)[0]->getExportId(), 9); + $this->assertEquals($this->messenger('async')->queue()->messages(ExportMessage::class)[1]->getExportId(), 10); } public function testShow() : void { @@ -133,7 +133,7 @@ public function testDelete() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); $this->assertFalse($fileSystem->exists($filePath)); } } diff --git a/tests/Controller/ImportTest.php b/tests/Controller/ImportTest.php index cc10e88..9935108 100644 --- a/tests/Controller/ImportTest.php +++ b/tests/Controller/ImportTest.php @@ -43,13 +43,13 @@ public function testNew() : void { } $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount + 3, $postCount); + $this->assertEquals($preCount + 3, $postCount); $this->messenger('async')->queue()->assertCount(3); $this->messenger('async')->queue()->assertContains(ImportMessage::class, 3); - $this->assertSame($this->messenger('async')->queue()->messages(ImportMessage::class)[0]->getImportId(), 9); - $this->assertSame($this->messenger('async')->queue()->messages(ImportMessage::class)[1]->getImportId(), 10); - $this->assertSame($this->messenger('async')->queue()->messages(ImportMessage::class)[2]->getImportId(), 11); + $this->assertEquals($this->messenger('async')->queue()->messages(ImportMessage::class)[0]->getImportId(), 9); + $this->assertEquals($this->messenger('async')->queue()->messages(ImportMessage::class)[1]->getImportId(), 10); + $this->assertEquals($this->messenger('async')->queue()->messages(ImportMessage::class)[2]->getImportId(), 11); } public function testPodcastNew() : void { @@ -79,12 +79,12 @@ public function testPodcastNew() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount + 2, $postCount); + $this->assertEquals($preCount + 2, $postCount); $this->messenger('async')->queue()->assertCount(2); $this->messenger('async')->queue()->assertContains(ImportMessage::class, 2); - $this->assertSame($this->messenger('async')->queue()->messages(ImportMessage::class)[0]->getImportId(), 12); - $this->assertSame($this->messenger('async')->queue()->messages(ImportMessage::class)[1]->getImportId(), 13); + $this->assertEquals($this->messenger('async')->queue()->messages(ImportMessage::class)[0]->getImportId(), 12); + $this->assertEquals($this->messenger('async')->queue()->messages(ImportMessage::class)[1]->getImportId(), 13); } public function testShow() : void { diff --git a/tests/Controller/InstitutionTest.php b/tests/Controller/InstitutionTest.php deleted file mode 100644 index 1db48ce..0000000 --- a/tests/Controller/InstitutionTest.php +++ /dev/null @@ -1,147 +0,0 @@ -client->request('GET', '/institutions'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/institutions'); - $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('New')->count()); - } - } - - public function testShow() : void { - // Anon - $crawler = $this->client->request('GET', '/institutions/1'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/institutions/1'); - $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); - } - } - - public function testTypeahead() : void { - // Anon - $this->client->request('GET', '/institutions/typeahead?q=' . self::SEARCH_QUERY); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $this->client->request('GET', '/institutions/typeahead?q=' . self::SEARCH_QUERY); - $response = $this->client->getResponse(); - $this->assertResponseIsSuccessful(); - $this->assertSame('application/json', $response->headers->get('content-type')); - $json = json_decode($response->getContent()); - $this->assertCount(4, $json); - } - } - - public function testSearch() : void { - // Anon - $crawler = $this->client->request('GET', '/institutions'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/institutions'); - $this->assertResponseIsSuccessful(); - - $form = $crawler->selectButton('btn-search')->form([ - 'q' => 'institution', - ]); - - $responseCrawler = $this->client->submit($form); - $this->assertSame(200, $this->client->getResponse()->getStatusCode()); - } - } - - public function testEdit() : void { - // Anon - $crawler = $this->client->request('GET', '/institutions/1/edit'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/institutions/1/edit'); - $this->assertResponseIsSuccessful(); - - $form = $formCrawler->selectButton('Update')->form([ - 'institution[country]' => 'Updated Country', - 'institution[name]' => 'Updated Name', - ]); - - $this->client->submit($form); - $this->assertResponseRedirects('/institutions/1', Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - } - } - - public function testNew() : void { - // Anon - $crawler = $this->client->request('GET', '/institutions/new'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - $newId = 5; - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/institutions/new'); - $this->assertResponseIsSuccessful(); - - $form = $formCrawler->selectButton('Create')->form([ - 'institution[country]' => 'Updated Country', - 'institution[name]' => "Updated Name{$newId}", - ]); - - $this->client->submit($form); - $this->assertResponseRedirects("/institutions/{$newId}", Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - $newId++; - } - } - - public function testDelete() : void { - $repo = self::getContainer()->get(InstitutionRepository::class); - $preCount = count($repo->findAll()); - - $this->login(UserFixtures::USER); - $crawler = $this->client->request('GET', '/institutions/1'); - $this->assertResponseIsSuccessful(); - $form = $crawler->selectButton('Delete')->form(); - $this->client->submit($form); - - $this->assertResponseRedirects('/institutions', Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - - $this->em->clear(); - $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); - } -} diff --git a/tests/Controller/PersonTest.php b/tests/Controller/PersonTest.php index 52f1d6a..16260a0 100644 --- a/tests/Controller/PersonTest.php +++ b/tests/Controller/PersonTest.php @@ -15,44 +15,39 @@ class PersonTest extends ControllerTestCase { public function testIndex() : void { // Anon - $crawler = $this->client->request('GET', '/people'); + $crawler = $this->client->request('GET', '/podcasts/2/people'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/people'); - $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('New')->count()); - } - } - - public function testShow() : void { - // Anon - $crawler = $this->client->request('GET', '/people/1'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/people'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + // User with podcast access, Admin + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/people/1'); + $crawler = $this->client->request('GET', '/podcasts/2/people'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); } } public function testTypeahead() : void { // Anon - $this->client->request('GET', '/people/typeahead?q=' . self::SEARCH_QUERY); + $this->client->request('GET', '/podcasts/2/people/typeahead?q=' . self::SEARCH_QUERY); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + // User without podcast access, + $this->login(UserFixtures::USER); + $this->client->request('GET', '/podcasts/2/people/typeahead?q=' . self::SEARCH_QUERY); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + + // User with podcast access, Admin + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $this->client->request('GET', '/people/typeahead?q=' . self::SEARCH_QUERY); + $this->client->request('GET', '/podcasts/2/people/typeahead?q=' . self::SEARCH_QUERY); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); - $this->assertSame('application/json', $response->headers->get('content-type')); + $this->assertEquals('application/json', $response->headers->get('content-type')); $json = json_decode($response->getContent()); $this->assertCount(4, $json); } @@ -60,13 +55,18 @@ public function testTypeahead() : void { public function testSearch() : void { // Anon - $crawler = $this->client->request('GET', '/people'); + $crawler = $this->client->request('GET', '/podcasts/2/people'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/people'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + + // User with podcast access, Admin + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/people'); + $crawler = $this->client->request('GET', '/podcasts/2/people'); $this->assertResponseIsSuccessful(); $form = $crawler->selectButton('btn-search')->form([ @@ -74,60 +74,76 @@ public function testSearch() : void { ]); $responseCrawler = $this->client->submit($form); - $this->assertSame(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); } } public function testEdit() : void { // Anon - $crawler = $this->client->request('GET', '/people/1/edit'); + $crawler = $this->client->request('GET', '/podcasts/2/people/5/edit'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/people/5/edit'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/people/1/edit'); + $formCrawler = $this->client->request('GET', '/podcasts/2/people/5/edit'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Update')->form([ + $form = $formCrawler->selectButton('Save')->form([ 'person[fullname]' => 'Updated Fullname', 'person[sortableName]' => 'Updated SortableName', 'person[location]' => 'Updated Location', 'person[bio]' => '

    Updated Text

    ', + 'person[institution]' => 'Updated Institution', ]); - $this->overrideField($form, 'person[institution]', '2'); $this->client->submit($form); - $this->assertResponseRedirects('/people/1', Response::HTTP_FOUND); + $this->assertResponseRedirects('/podcasts/2/people', Response::HTTP_FOUND); $responseCrawler = $this->client->followRedirect(); $this->assertResponseIsSuccessful(); } } public function testNew() : void { + $repo = self::getContainer()->get(PersonRepository::class); + $newId = count($repo->findAll()) + 1; + // Anon - $crawler = $this->client->request('GET', '/people/new'); + $crawler = $this->client->request('GET', '/podcasts/2/people/new'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/people/new'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + // User without podcast access, User with podcast access, Admin - $newId = 5; - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/people/new'); + $formCrawler = $this->client->request('GET', '/podcasts/2/people/new'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Create')->form([ + $form = $formCrawler->selectButton('Save')->form([ 'person[fullname]' => "Updated Fullname {$newId}", 'person[sortableName]' => 'Updated SortableName', 'person[location]' => 'Updated Location', 'person[bio]' => '

    Updated Text

    ', + 'person[institution]' => 'Updated Institution', ]); - $this->overrideField($form, 'person[institution]', '2'); $this->client->submit($form); - $this->assertResponseRedirects("/people/{$newId}", Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); + $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); + $this->assertEquals('application/json', $response->headers->get('content-type')); + $json = (array) json_decode($response->getContent()); + $this->assertEquals($json, [ + 'success' => true, + ]); $newId++; } } @@ -136,18 +152,18 @@ public function testDelete() : void { $repo = self::getContainer()->get(PersonRepository::class); $preCount = count($repo->findAll()); - $this->login(UserFixtures::USER); - $crawler = $this->client->request('GET', '/people/1'); + $this->login(UserExtraFixtures::USER_WITH_ACCESS); + $crawler = $this->client->request('GET', '/podcasts/2/people'); $this->assertResponseIsSuccessful(); - $form = $crawler->selectButton('Delete')->form(); - $this->client->submit($form); - $this->assertResponseRedirects('/people', Response::HTTP_FOUND); + $form = $crawler->filter('form[action="/podcasts/2/people/8"]')->form(); + $this->client->submit($form); + $this->assertResponseRedirects('/podcasts/2/people', Response::HTTP_FOUND); $responseCrawler = $this->client->followRedirect(); $this->assertResponseIsSuccessful(); $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); } } diff --git a/tests/Controller/PodcastTest.php b/tests/Controller/PodcastTest.php index 5f68e3d..904d327 100644 --- a/tests/Controller/PodcastTest.php +++ b/tests/Controller/PodcastTest.php @@ -23,8 +23,7 @@ public function testIndex() : void { $this->login($loginCredentials); $crawler = $this->client->request('GET', '/podcasts'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('New')->count()); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('RSS Import')->count()); + $this->assertEquals(1, $crawler->filter('.page-actions')->selectLink('Import New Podcast from RSS Feed')->count()); } } @@ -44,7 +43,7 @@ public function testSearch() : void { ]); $responseCrawler = $this->client->submit($form); - $this->assertSame(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); } } @@ -63,16 +62,17 @@ public function testShow() : void { $this->login($loginCredentials); $crawler = $this->client->request('GET', '/podcasts/2'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); - $this->assertSame(1, $crawler->selectLink('New Episode')->count()); - $this->assertSame(1, $crawler->selectLink('New Season')->count()); - $this->assertSame(4, $crawler->filter('div[role="tablist"] span[role="tab"]')->count()); + $this->assertEquals(1, $crawler->filter('.page-actions')->selectLink('Edit Podcast')->count()); + $this->assertEquals(2, $crawler->selectLink('Edit Podcast')->count()); + $this->assertEquals(1, $crawler->selectLink('New Episode')->count()); + $this->assertEquals(1, $crawler->selectLink('New Season')->count()); + $this->assertEquals(4, $crawler->filter('div[role="tablist"] span[role="tab"]')->count()); foreach (range(1, 4) as $seasonId) { $expectedEpisodes = 2 === $seasonId ? 4 : 0; - $this->assertSame(1, $crawler->filter("#nav-season-{$seasonId}-tab")->count()); - $this->assertSame(1, $crawler->filter("#nav-season-{$seasonId} .season-actions")->selectLink('Edit')->count()); - $this->assertSame($expectedEpisodes, $crawler->filter("#nav-season-{$seasonId} table tbody tr")->count()); - $this->assertSame($expectedEpisodes, $crawler->filter("#nav-season-{$seasonId} table tbody tr")->selectLink('Edit')->count()); + $this->assertEquals(1, $crawler->filter("#nav-season-{$seasonId}-tab")->count()); + $this->assertEquals(1, $crawler->filter("#nav-season-{$seasonId} .season-actions")->selectLink('Edit Season')->count()); + $this->assertEquals($expectedEpisodes, $crawler->filter("#nav-season-{$seasonId} table tbody tr")->count()); + $this->assertEquals($expectedEpisodes, $crawler->filter("#nav-season-{$seasonId} table tbody tr")->selectLink('Edit Episode')->count()); } } } @@ -93,7 +93,7 @@ public function testEdit() : void { $formCrawler = $this->client->request('GET', '/podcasts/1/edit'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Update')->form([ + $form = $formCrawler->selectButton('Save')->form([ 'podcast[title]' => 'Updated Title', 'podcast[subTitle]' => 'Updated subTitle', 'podcast[explicit]' => 1, @@ -113,39 +113,6 @@ public function testEdit() : void { } } - public function testNew() : void { - // Anon - $crawler = $this->client->request('GET', '/podcasts/new'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - - // User without podcast access, User with podcast access, Admin - $newId = 9; - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/podcasts/new'); - $this->assertResponseIsSuccessful(); - - $form = $formCrawler->selectButton('Create')->form([ - 'podcast[title]' => 'Updated Title', - 'podcast[subTitle]' => 'Updated subTitle', - 'podcast[explicit]' => 1, - 'podcast[languageCode]' => 'en', - 'podcast[description]' => '

    Updated Text

    ', - 'podcast[copyright]' => '

    Updated Text

    ', - 'podcast[license]' => '

    Updated Text

    ', - 'podcast[website]' => 'https://example.com', - 'podcast[rss]' => 'https://example.com', - ]); - $this->overrideField($form, 'podcast[publisher]', '2'); - - $this->client->submit($form); - $this->assertResponseRedirects("/podcasts/{$newId}", Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); - $this->assertResponseIsSuccessful(); - $newId++; - } - } - public function testDelete() : void { $repo = self::getContainer()->get(PodcastRepository::class); $preCount = count($repo->findAll()); @@ -162,6 +129,6 @@ public function testDelete() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); } } diff --git a/tests/Controller/PublisherTest.php b/tests/Controller/PublisherTest.php index 16536ab..a26323c 100644 --- a/tests/Controller/PublisherTest.php +++ b/tests/Controller/PublisherTest.php @@ -15,44 +15,39 @@ class PublisherTest extends ControllerTestCase { public function testIndex() : void { // Anon - $crawler = $this->client->request('GET', '/publishers'); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { - $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/publishers'); - $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('New')->count()); - } - } - - public function testShow() : void { - // Anon - $crawler = $this->client->request('GET', '/publishers/1'); - $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); - // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + // User with podcast access, Admin + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/publishers/1'); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); } } public function testTypeahead() : void { // Anon - $this->client->request('GET', '/publishers/typeahead?q=' . self::SEARCH_QUERY); + $this->client->request('GET', '/podcasts/2/publishers/typeahead?q=' . self::SEARCH_QUERY); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $this->client->request('GET', '/podcasts/2/publishers/typeahead?q=' . self::SEARCH_QUERY); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $this->client->request('GET', '/publishers/typeahead?q=' . self::SEARCH_QUERY); + $this->client->request('GET', '/podcasts/2/publishers/typeahead?q=' . self::SEARCH_QUERY); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); - $this->assertSame('application/json', $response->headers->get('content-type')); + $this->assertEquals('application/json', $response->headers->get('content-type')); $json = json_decode($response->getContent()); $this->assertCount(4, $json); } @@ -60,13 +55,18 @@ public function testTypeahead() : void { public function testSearch() : void { // Anon - $crawler = $this->client->request('GET', '/publishers'); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $crawler = $this->client->request('GET', '/publishers'); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); $this->assertResponseIsSuccessful(); $form = $crawler->selectButton('btn-search')->form([ @@ -74,22 +74,27 @@ public function testSearch() : void { ]); $responseCrawler = $this->client->submit($form); - $this->assertSame(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); } } public function testEdit() : void { // Anon - $crawler = $this->client->request('GET', '/publishers/1/edit'); + $crawler = $this->client->request('GET', '/podcasts/2/publishers/5/edit'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $formCrawler = $this->client->request('GET', '/podcasts/2/publishers/5/edit'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + // User without podcast access, User with podcast access, Admin - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/publishers/1/edit'); + $formCrawler = $this->client->request('GET', '/podcasts/2/publishers/5/edit'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Update')->form([ + $form = $formCrawler->selectButton('Save')->form([ 'publisher[name]' => 'Updated Name', 'publisher[location]' => 'Updated Location', 'publisher[website]' => 'http://example.com', @@ -98,25 +103,32 @@ public function testEdit() : void { ]); $this->client->submit($form); - $this->assertResponseRedirects('/publishers/1', Response::HTTP_FOUND); + $this->assertResponseRedirects('/podcasts/2/publishers', Response::HTTP_FOUND); $responseCrawler = $this->client->followRedirect(); $this->assertResponseIsSuccessful(); } } public function testNew() : void { + $repo = self::getContainer()->get(PublisherRepository::class); + $newId = count($repo->findAll()) + 1; + // Anon - $crawler = $this->client->request('GET', '/publishers/new'); + $crawler = $this->client->request('GET', '/podcasts/2/publishers/new'); $this->assertResponseRedirects('http://localhost/login', Response::HTTP_FOUND); + // User without podcast access, + $this->login(UserFixtures::USER); + $crawler = $this->client->request('GET', '/podcasts/2/publishers/new'); + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + // User without podcast access, User with podcast access, Admin - $newId = 5; - foreach ([UserFixtures::USER, UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { + foreach ([UserExtraFixtures::USER_WITH_ACCESS, UserFixtures::ADMIN] as $loginCredentials) { $this->login($loginCredentials); - $formCrawler = $this->client->request('GET', '/publishers/new'); + $formCrawler = $this->client->request('GET', '/podcasts/2/publishers/new'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Create')->form([ + $form = $formCrawler->selectButton('Save')->form([ 'publisher[name]' => "Updated Name{$newId}", 'publisher[location]' => 'Updated Location', 'publisher[website]' => 'http://example.com', @@ -125,9 +137,13 @@ public function testNew() : void { ]); $this->client->submit($form); - $this->assertResponseRedirects("/publishers/{$newId}", Response::HTTP_FOUND); - $responseCrawler = $this->client->followRedirect(); + $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); + $this->assertEquals('application/json', $response->headers->get('content-type')); + $json = (array) json_decode($response->getContent()); + $this->assertEquals($json, [ + 'success' => true, + ]); $newId++; } } @@ -136,18 +152,18 @@ public function testDelete() : void { $repo = self::getContainer()->get(PublisherRepository::class); $preCount = count($repo->findAll()); - $this->login(UserFixtures::USER); - $crawler = $this->client->request('GET', '/publishers/1'); + $this->login(UserExtraFixtures::USER_WITH_ACCESS); + $crawler = $this->client->request('GET', '/podcasts/2/publishers'); $this->assertResponseIsSuccessful(); - $form = $crawler->selectButton('Delete')->form(); - $this->client->submit($form); - $this->assertResponseRedirects('/publishers', Response::HTTP_FOUND); + $form = $crawler->filter('form[action="/podcasts/2/publishers/8"]')->form(); + $this->client->submit($form); + $this->assertResponseRedirects('/podcasts/2/publishers', Response::HTTP_FOUND); $responseCrawler = $this->client->followRedirect(); $this->assertResponseIsSuccessful(); $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); } } diff --git a/tests/Controller/SeasonTest.php b/tests/Controller/SeasonTest.php index de97373..549d7d4 100644 --- a/tests/Controller/SeasonTest.php +++ b/tests/Controller/SeasonTest.php @@ -28,7 +28,7 @@ public function testShow() : void { $this->login($loginCredentials); $crawler = $this->client->request('GET', '/podcasts/2/seasons/1'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('.page-actions')->selectLink('Edit')->count()); + $this->assertEquals(1, $crawler->filter('.page-actions')->selectLink('Edit Season')->count()); } } @@ -48,7 +48,7 @@ public function testTypeahead() : void { $this->client->request('GET', '/podcasts/2/seasons/typeahead?q=' . self::SEARCH_QUERY); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); - $this->assertSame('application/json', $response->headers->get('content-type')); + $this->assertEquals('application/json', $response->headers->get('content-type')); $json = json_decode($response->getContent()); $this->assertCount(4, $json); } @@ -70,13 +70,13 @@ public function testEdit() : void { $formCrawler = $this->client->request('GET', '/podcasts/2/seasons/1/edit'); $this->assertResponseIsSuccessful(); - $form = $formCrawler->selectButton('Update')->form([ + $form = $formCrawler->selectButton('Save')->form([ 'season[number]' => 10, 'season[title]' => 'Updated Title', 'season[subTitle]' => 'Updated subTitle', 'season[description]' => '

    Updated Text

    ', ]); - $this->overrideField($form, 'season[publisher]', '2'); + $this->overrideField($form, 'season[publisher]', '6'); $this->client->submit($form); $this->assertResponseRedirects('/podcasts/2/seasons/1', Response::HTTP_FOUND); @@ -108,7 +108,7 @@ public function testNew() : void { 'season[subTitle]' => 'Updated subTitle', 'season[description]' => '

    Updated Text

    ', ]); - $this->overrideField($form, 'season[publisher]', '2'); + $this->overrideField($form, 'season[publisher]', '6'); $this->client->submit($form); $this->assertResponseRedirects('/podcasts/2', Response::HTTP_FOUND); @@ -134,6 +134,6 @@ public function testDelete() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); } } diff --git a/tests/Controller/ShareTest.php b/tests/Controller/ShareTest.php index 172b1e1..9c347b0 100644 --- a/tests/Controller/ShareTest.php +++ b/tests/Controller/ShareTest.php @@ -28,7 +28,7 @@ public function testIndex() : void { $this->login($loginCredentials); $crawler = $this->client->request('GET', '/podcasts/2/shares'); $this->assertResponseIsSuccessful(); - $this->assertSame(1, $crawler->filter('form')->selectButton('Share')->count()); + $this->assertEquals(1, $crawler->filter('form')->selectButton('Share')->count()); } } @@ -48,7 +48,7 @@ public function testTypeahead() : void { $this->client->request('GET', '/podcasts/2/shares/typeahead?q=' . self::SEARCH_QUERY); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); - $this->assertSame('application/json', $response->headers->get('content-type')); + $this->assertEquals('application/json', $response->headers->get('content-type')); $json = json_decode($response->getContent()); $this->assertCount(4, $json); } @@ -96,6 +96,6 @@ public function testDelete() : void { $this->em->clear(); $postCount = count($repo->findAll()); - $this->assertSame($preCount - 1, $postCount); + $this->assertEquals($preCount - 1, $postCount); } } diff --git a/tests/Repository/ContributorRoleRepositoryTest.php b/tests/Repository/ContributorRoleRepositoryTest.php deleted file mode 100644 index 0455080..0000000 --- a/tests/Repository/ContributorRoleRepositoryTest.php +++ /dev/null @@ -1,38 +0,0 @@ -assertInstanceOf(ContributorRoleRepository::class, $this->repo); - } - - public function testIndexQuery() : void { - $query = $this->repo->indexQuery(); - $this->assertCount(4, $query->execute()); - } - - public function testTypeaheadQuery() : void { - $query = $this->repo->typeaheadQuery(self::SEARCH_QUERY); - $this->assertCount(4, $query->execute()); - } - - public function testSearchQuery() : void { - $query = $this->repo->searchQuery(self::SEARCH_QUERY); - $this->assertCount(4, $query->execute()); - } - - protected function setUp() : void { - parent::setUp(); - $this->repo = self::getContainer()->get(ContributorRoleRepository::class); - } -} diff --git a/tests/Repository/InstitutionRepositoryTest.php b/tests/Repository/InstitutionRepositoryTest.php deleted file mode 100644 index 0e4ea63..0000000 --- a/tests/Repository/InstitutionRepositoryTest.php +++ /dev/null @@ -1,38 +0,0 @@ -assertInstanceOf(InstitutionRepository::class, $this->repo); - } - - public function testIndexQuery() : void { - $query = $this->repo->indexQuery(); - $this->assertCount(4, $query->execute()); - } - - public function testTypeaheadQuery() : void { - $query = $this->repo->typeaheadQuery(self::SEARCH_QUERY); - $this->assertCount(4, $query->execute()); - } - - public function testSearchQuery() : void { - $query = $this->repo->searchQuery(self::SEARCH_QUERY); - $this->assertCount(4, $query->execute()); - } - - protected function setUp() : void { - parent::setUp(); - $this->repo = self::getContainer()->get(InstitutionRepository::class); - } -} diff --git a/tests/Repository/PersonRepositoryTest.php b/tests/Repository/PersonRepositoryTest.php index b9667a5..81c2d2d 100644 --- a/tests/Repository/PersonRepositoryTest.php +++ b/tests/Repository/PersonRepositoryTest.php @@ -4,6 +4,7 @@ namespace App\Tests\Repository; +use App\Repository\PodcastRepository; use App\Repository\PersonRepository; use Nines\UtilBundle\TestCase\ServiceTestCase; @@ -17,17 +18,27 @@ public function testSetUp() : void { } public function testIndexQuery() : void { - $query = $this->repo->indexQuery(); + $podcastRepository = self::getContainer()->get(PodcastRepository::class); + $podcast = $podcastRepository->find(2); + + $query = $this->repo->indexQuery($podcast); + $this->assertCount(4, $query->execute()); } public function testTypeaheadQuery() : void { - $query = $this->repo->typeaheadQuery(self::SEARCH_QUERY); + $podcastRepository = self::getContainer()->get(PodcastRepository::class); + $podcast = $podcastRepository->find(2); + + $query = $this->repo->typeaheadQuery($podcast, self::SEARCH_QUERY); $this->assertCount(4, $query->execute()); } public function testSearchQuery() : void { - $query = $this->repo->searchQuery(self::SEARCH_QUERY); + $podcastRepository = self::getContainer()->get(PodcastRepository::class); + $podcast = $podcastRepository->find(2); + + $query = $this->repo->searchQuery($podcast, self::SEARCH_QUERY); $this->assertCount(4, $query->execute()); } diff --git a/tests/Repository/PublisherRepositoryTest.php b/tests/Repository/PublisherRepositoryTest.php index ed69a27..6baec20 100644 --- a/tests/Repository/PublisherRepositoryTest.php +++ b/tests/Repository/PublisherRepositoryTest.php @@ -4,6 +4,7 @@ namespace App\Tests\Repository; +use App\Repository\PodcastRepository; use App\Repository\PublisherRepository; use Nines\UtilBundle\TestCase\ServiceTestCase; @@ -17,17 +18,26 @@ public function testSetUp() : void { } public function testIndexQuery() : void { - $query = $this->repo->indexQuery(); + $podcastRepository = self::getContainer()->get(PodcastRepository::class); + $podcast = $podcastRepository->find(2); + + $query = $this->repo->indexQuery($podcast); $this->assertCount(4, $query->execute()); } public function testTypeaheadQuery() : void { - $query = $this->repo->typeaheadQuery(self::SEARCH_QUERY); + $podcastRepository = self::getContainer()->get(PodcastRepository::class); + $podcast = $podcastRepository->find(2); + + $query = $this->repo->typeaheadQuery($podcast, self::SEARCH_QUERY); $this->assertCount(4, $query->execute()); } public function testSearchQuery() : void { - $query = $this->repo->searchQuery(self::SEARCH_QUERY); + $podcastRepository = self::getContainer()->get(PodcastRepository::class); + $podcast = $podcastRepository->find(2); + + $query = $this->repo->searchQuery($podcast, self::SEARCH_QUERY); $this->assertCount(4, $query->execute()); }