From 4cd9798949d3f6af4061396f8010e1c482971612 Mon Sep 17 00:00:00 2001 From: Julio Date: Mon, 6 Sep 2021 16:43:23 +0200 Subject: [PATCH] Course category: Add tests, fix CRUD course category asset + add migration --- public/main/admin/course_category.php | 8 +- public/main/inc/lib/course_category.lib.php | 51 ++----- src/CoreBundle/Entity/CourseCategory.php | 30 ++-- .../EventListener/AssetListener.php | 7 +- .../Schema/V200/Version20191101132000.php | 61 +++++++- .../Repository/CourseCategoryRepository.php | 11 ++ .../CourseCategoryRepositoryTest.php | 141 +++++++++++++++++- 7 files changed, 242 insertions(+), 67 deletions(-) diff --git a/public/main/admin/course_category.php b/public/main/admin/course_category.php index 91c8e3b65f..8db8771036 100644 --- a/public/main/admin/course_category.php +++ b/public/main/admin/course_category.php @@ -92,11 +92,11 @@ switch ($action) { // Delete Picture Category $deletePicture = $_POST['delete_picture'] ?? ''; - var_dump($deletePicture); - if ($deletePicture) { - CourseCategory::deleteImage($categoryEntity); + + if ($deletePicture && $categoryEntity) { + $categoryRepo->deleteAsset($categoryEntity); } -exit; + if (isset($_FILES['image']) && $categoryEntity) { $crop = $_POST['picture_crop_result'] ?? ''; CourseCategory::saveImage($categoryEntity, $_FILES['image'], $crop); diff --git a/public/main/inc/lib/course_category.lib.php b/public/main/inc/lib/course_category.lib.php index c2a46fa0e2..338aba5c56 100644 --- a/public/main/inc/lib/course_category.lib.php +++ b/public/main/inc/lib/course_category.lib.php @@ -203,27 +203,15 @@ class CourseCategory Database::query($sql); } - public static function delete($categoryId): bool + public static function edit($categoryId, $name, $canHaveCourses, $code, $description): ?CourseCategoryEntity { $repo = Container::getCourseCategoryRepository(); $category = $repo->find($categoryId); if (null === $category) { - return false; + return null; } - $repo->delete($category); - - return true; - } - - public static function edit($categoryId, $name, $canHaveCourses, $code, $description): CourseCategoryEntity - { - $name = trim(Database::escape_string($name)); - $canHaveCourses = Database::escape_string($canHaveCourses); - - $repo = Container::getCourseCategoryRepository(); - $category = $repo->find($categoryId); - + $name = trim($name); $category ->setCode($name) ->setName($name) @@ -837,26 +825,6 @@ class CourseCategory return $nameTools; } - public static function deleteImage(CourseCategoryEntity $category) - { - $assetId = $category->getImage(); - if (empty($assetId)) { - return false; - } - - $assetRepo = Container::getAssetRepository(); - /** @var Asset $asset */ - $asset = $assetRepo->find($assetId); - if (null !== $asset) { - $category->setImage(''); - $assetRepo->delete($asset); - $repo = Container::getCourseCategoryRepository(); - $repo->save($category); - } - - return true; - } - /** * Save image for a course category. * @@ -865,19 +833,18 @@ class CourseCategory public static function saveImage(CourseCategoryEntity $category, $fileData, $crop = '') { if (isset($fileData['tmp_name']) && !empty($fileData['tmp_name'])) { - self::deleteImage($category); + $repo = Container::getCourseCategoryRepository(); + $repo->deleteAsset($category); - $repo = Container::getAssetRepository(); - $asset = new Asset(); - $asset + $assetRepo = Container::getAssetRepository(); + $asset = (new Asset()) ->setCategory(Asset::COURSE_CATEGORY) ->setTitle($fileData['name']) ->setCrop($crop) ; - $asset = $repo->createFromRequest($asset, $fileData); + $asset = $assetRepo->createFromRequest($asset, $fileData); - $category->setImage($asset->getId()); - $repo = Container::getCourseCategoryRepository(); + $category->setAsset($asset); $repo->save($category); } } diff --git a/src/CoreBundle/Entity/CourseCategory.php b/src/CoreBundle/Entity/CourseCategory.php index 27faf3656b..1fab515d10 100644 --- a/src/CoreBundle/Entity/CourseCategory.php +++ b/src/CoreBundle/Entity/CourseCategory.php @@ -57,17 +57,17 @@ class CourseCategory protected Collection $children; /** - * @Assert\NotBlank() * @Groups({"course_category:read", "course_category:write", "course:read"}) * @ORM\Column(name="name", type="text", nullable=false) */ + #[Assert\NotBlank] protected string $name; /** - * @Assert\NotBlank() * @Groups({"course_category:read", "course_category:write", "course:read"}) * @ORM\Column(name="code", type="string", length=40, nullable=false) */ + #[Assert\NotBlank] protected string $code; /** @@ -97,9 +97,10 @@ class CourseCategory protected ?string $authCatChild = null; /** - * @ORM\Column(name="image", type="string", length=255, nullable=true) + * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Asset", inversedBy="courseCategories", cascade={"remove"} ) + * @ORM\JoinColumn(name="asset_id", referencedColumnName="id", onDelete="SET NULL") */ - protected ?string $image = null; + protected ?Asset $asset = null; /** * @Groups({"course_category:read", "course_category:write"}) @@ -265,26 +266,31 @@ class CourseCategory return $this->authCatChild; } - public function getImage(): ?string + public function getDescription(): ?string { - return $this->image; + return $this->description; } - public function setImage(string $image): self + public function setDescription(string $description): self { - $this->image = $image; + $this->description = $description; return $this; } - public function getDescription(): ?string + public function getAsset(): ?Asset { - return $this->description; + return $this->asset; } - public function setDescription(string $description): self + public function hasAsset(): bool { - $this->description = $description; + return null !== $this->asset; + } + + public function setAsset(?Asset $asset): self + { + $this->asset = $asset; return $this; } diff --git a/src/CoreBundle/EventListener/AssetListener.php b/src/CoreBundle/EventListener/AssetListener.php index 25ac0c4ba9..a19cf1a653 100644 --- a/src/CoreBundle/EventListener/AssetListener.php +++ b/src/CoreBundle/EventListener/AssetListener.php @@ -25,13 +25,14 @@ class AssetListener $asset = $event->getObject(); if ($asset instanceof Asset) { $mapping = $event->getMapping(); - $folder = $mapping->getFile($asset)->getFilename(); + $filePath = $asset->getCategory().'/'.$asset->getFile()->getFilename(); + $this->assetRepository->getFileSystem()->deleteDirectory($filePath); // Deletes scorm folder: example: assets/scorm/myABC . - if (!empty($folder) && Asset::SCORM === $asset->getCategory()) { + /*if (!empty($folder) && Asset::SCORM === $asset->getCategory()) { $folder = Asset::SCORM.'/'.$folder; $this->assetRepository->getFileSystem()->deleteDirectory($folder); - } + }*/ } } } diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php b/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php index cd16c8bb6b..cf11d67a67 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php @@ -6,8 +6,13 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\Migrations\Schema\V200; +use Chamilo\CoreBundle\Entity\Asset; +use Chamilo\CoreBundle\Entity\CourseCategory; use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; +use Chamilo\CoreBundle\Repository\CourseCategoryRepository; +use Chamilo\Kernel; use Doctrine\DBAL\Schema\Schema; +use Symfony\Component\HttpFoundation\File\UploadedFile; class Version20191101132000 extends AbstractMigrationChamilo { @@ -81,8 +86,12 @@ class Version20191101132000 extends AbstractMigrationChamilo if (!$table->hasIndex('course_rel_user_c_id_user_id')) { $this->addSql('CREATE INDEX course_rel_user_c_id_user_id ON course_rel_user (id, c_id, user_id)'); } + + $table = $schema->getTable('course_category'); + //$this->addSql('ALTER TABLE course DROP category_code'); - $connection = $this->getEntityManager()->getConnection(); + $em = $this->getEntityManager(); + $connection = $em->getConnection(); $sql = 'SELECT * FROM course_category'; $result = $connection->executeQuery($sql); $all = $result->fetchAllAssociative(); @@ -102,15 +111,57 @@ class Version20191101132000 extends AbstractMigrationChamilo $this->addSql('ALTER TABLE course_category CHANGE parent_id parent_id INT DEFAULT NULL;'); - $table = $schema->getTable('course_category'); if (false === $table->hasForeignKey('FK_AFF87497727ACA70')) { $this->addSql( - 'ALTER TABLE course_category ADD CONSTRAINT FK_AFF87497727ACA70 FOREIGN KEY (parent_id) REFERENCES course_category (id) ON DELETE CASCADE' + 'ALTER TABLE course_category ADD CONSTRAINT FK_AFF87497727ACA70 FOREIGN KEY (parent_id) REFERENCES course_category (id) ON DELETE SET NULL ' ); } - if (!$table->hasColumn('image')) { - $this->addSql('ALTER TABLE course_category ADD image VARCHAR(255) DEFAULT NULL'); + + if (!$table->hasColumn('asset_id')) { + $this->addSql('ALTER TABLE course_category ADD asset_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE course_category ADD CONSTRAINT FK_AFF874975DA1941 FOREIGN KEY (asset_id) REFERENCES asset (id)'); + $this->addSql('CREATE INDEX IDX_AFF874975DA1941 ON course_category (asset_id);'); } + + $container = $this->getContainer(); + + /** @var Kernel $kernel */ + $kernel = $container->get('kernel'); + $rootPath = $kernel->getProjectDir(); + + $repo = $container->get(CourseCategoryRepository::class); + + if ($table->hasColumn('image')) { + foreach ($all as $category) { + if (!empty($category['image'])) { + /** @var CourseCategory $categoryEntity */ + $categoryEntity = $repo->find($category['id']); + + if ($categoryEntity->hasAsset()) { + continue; + } + + $filePath = $rootPath.'/app/upload/course_category/'.$category['image']; + if ($this->fileExists($filePath)) { + $fileName = basename($filePath); + $mimeType = mime_content_type($filePath); + $file = new UploadedFile($filePath, $fileName, $mimeType, null, true); + $asset = (new Asset()) + ->setCategory(Asset::COURSE_CATEGORY) + ->setTitle($fileName) + ->setFile($file) + ; + $em->persist($asset); + $em->flush(); + $categoryEntity->setAsset($asset); + + $em->persist($categoryEntity); + $em->flush(); + } + } + } + } + if (!$table->hasColumn('description')) { $this->addSql('ALTER TABLE course_category ADD description LONGTEXT DEFAULT NULL'); } diff --git a/src/CoreBundle/Repository/CourseCategoryRepository.php b/src/CoreBundle/Repository/CourseCategoryRepository.php index 75a3b5c02f..bf739bbe60 100644 --- a/src/CoreBundle/Repository/CourseCategoryRepository.php +++ b/src/CoreBundle/Repository/CourseCategoryRepository.php @@ -135,10 +135,21 @@ class CourseCategoryRepository extends ServiceEntityRepository $em->flush(); } + public function deleteAsset(CourseCategory $category): void + { + $em = $this->getEntityManager(); + if ($category->hasAsset()) { + $asset = $category->getAsset(); + $em->remove($asset); + $em->flush(); + } + } + public function delete(CourseCategory $category): void { $em = $this->getEntityManager(); $em->remove($category); + $this->deleteAsset($category); $em->flush(); } diff --git a/tests/CoreBundle/Repository/CourseCategoryRepositoryTest.php b/tests/CoreBundle/Repository/CourseCategoryRepositoryTest.php index b9fcf98a8a..c1e240cb2e 100644 --- a/tests/CoreBundle/Repository/CourseCategoryRepositoryTest.php +++ b/tests/CoreBundle/Repository/CourseCategoryRepositoryTest.php @@ -6,10 +6,13 @@ declare(strict_types=1); namespace Chamilo\Tests\CoreBundle\Repository; +use Chamilo\CoreBundle\Entity\Asset; use Chamilo\CoreBundle\Entity\CourseCategory; +use Chamilo\CoreBundle\Repository\AssetRepository; use Chamilo\CoreBundle\Repository\CourseCategoryRepository; use Chamilo\Tests\AbstractApiTest; use Chamilo\Tests\ChamiloTestTrait; +use Symfony\Component\HttpFoundation\Response; class CourseCategoryRepositoryTest extends AbstractApiTest { @@ -21,6 +24,7 @@ class CourseCategoryRepositoryTest extends AbstractApiTest $em = $this->getManager(); $repo = self::getContainer()->get(CourseCategoryRepository::class); + $defaultCount = $repo->count([]); $item = (new CourseCategory()) ->setCode('Course cat') @@ -32,6 +36,141 @@ class CourseCategoryRepositoryTest extends AbstractApiTest // On a fresh installation there are already 3 categories. // See the src/CoreBundle/DataFixtures/CourseCategoryFixtures.php - $this->assertSame(4, $repo->count([])); + $this->assertSame($defaultCount + 1, $repo->count([])); + } + + public function testCreateWithAsset(): void + { + self::bootKernel(); + + $em = $this->getManager(); + + /** @var CourseCategoryRepository $repoCourseCategory */ + $repoCourseCategory = self::getContainer()->get(CourseCategoryRepository::class); + $defaultCount = $repoCourseCategory->count([]); + + /** @var AssetRepository $assetRepo */ + $assetRepo = self::getContainer()->get(AssetRepository::class); + + $file = $this->getUploadedFile(); + + // Create asset. + $asset = (new Asset()) + ->setTitle('file') + ->setCategory(Asset::COURSE_CATEGORY) + ->setFile($file) + ; + $em->persist($asset); + + $item = (new CourseCategory()) + ->setCode('cat') + ->setName('cat') + ->setAsset($asset) + ; + + $this->assertHasNoEntityViolations($item); + $repoCourseCategory->save($item); + + $this->assertSame($defaultCount + 1, $repoCourseCategory->count([])); + $this->assertTrue($item->hasAsset()); + $this->assertSame(1, $assetRepo->count([])); + + $repoCourseCategory->delete($item); + + $this->assertSame($defaultCount, $repoCourseCategory->count([])); + } + + public function testDelete(): void + { + self::bootKernel(); + + $em = $this->getManager(); + + /** @var CourseCategoryRepository $repoCourseCategory */ + $repoCourseCategory = self::getContainer()->get(CourseCategoryRepository::class); + $defaultCount = $repoCourseCategory->count([]); + + /** @var AssetRepository $assetRepo */ + $assetRepo = self::getContainer()->get(AssetRepository::class); + + $file = $this->getUploadedFile(); + + // Create asset. + $asset = (new Asset()) + ->setTitle('file') + ->setCategory(Asset::COURSE_CATEGORY) + ->setFile($file) + ; + $em->persist($asset); + + $courseCategory = (new CourseCategory()) + ->setCode('cat') + ->setName('cat') + ->setAsset($asset) + ; + $repoCourseCategory->save($courseCategory); + + $url = $assetRepo->getAssetUrl($asset); + $this->assertNotEmpty($url); + $content = $assetRepo->getAssetContent($asset); + $this->assertNotEmpty($content); + + $client = static::createClient(); + $client->request('GET', $url); + $this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode()); + + $this->assertSame(1, $assetRepo->count([])); + $em->clear(); + + $courseCategory = $repoCourseCategory->find($courseCategory->getId()); + + $repoCourseCategory->delete($courseCategory); + + $this->assertSame(0, $assetRepo->count([])); + $this->assertSame($defaultCount, $repoCourseCategory->count([])); + + $content = $assetRepo->getAssetContent($asset); + $this->assertEmpty($content); + } + + public function testEditAndDeleteAsset(): void + { + self::bootKernel(); + + $em = $this->getManager(); + + /** @var CourseCategoryRepository $repoCourseCategory */ + $repoCourseCategory = self::getContainer()->get(CourseCategoryRepository::class); + $defaultCount = $repoCourseCategory->count([]); + + /** @var AssetRepository $assetRepo */ + $assetRepo = self::getContainer()->get(AssetRepository::class); + + $file = $this->getUploadedFile(); + + // Create asset. + $asset = (new Asset()) + ->setTitle('file') + ->setCategory(Asset::COURSE_CATEGORY) + ->setFile($file) + ; + $em->persist($asset); + + $courseCategory = (new CourseCategory()) + ->setCode('cat') + ->setName('cat') + ->setAsset($asset) + ; + $repoCourseCategory->save($courseCategory); + + $this->assertSame($defaultCount + 1, $repoCourseCategory->count([])); + + $this->assertSame(1, $assetRepo->count([])); + + $courseCategory = $repoCourseCategory->find($courseCategory->getId()); + $repoCourseCategory->deleteAsset($courseCategory); + + $this->assertSame(0, $assetRepo->count([])); + $this->assertSame($defaultCount + 1, $repoCourseCategory->count([])); } }