Add Asset.php entity to handle SCORMS, CSS, extra files.

pull/3741/head
Julio Montoya 4 years ago
parent 4e7d2f31e0
commit f57641bb27
  1. 7
      config/packages/oneup_flysystem.yaml
  2. 12
      config/packages/vich_uploader.yaml
  3. 2
      config/services.yaml
  4. 78
      src/CoreBundle/Component/Utils/AssetDirectoryNamer.php
  5. 383
      src/CoreBundle/Entity/Asset.php
  6. 45
      src/CoreBundle/Framework/Container.php
  7. 103
      src/CoreBundle/Repository/AssetRepository.php

@ -2,8 +2,10 @@ oneup_flysystem:
cache:
my_cache:
memory: ~
adapters:
asset_adapter:
local:
directory: '%kernel.project_dir%/var/upload/assets'
resource_adapter:
local:
directory: '%kernel.project_dir%/var/upload/resource'
@ -11,6 +13,9 @@ oneup_flysystem:
local:
directory: '%kernel.project_dir%/var/cache/resource'
filesystems:
assets:
adapter: asset_adapter
mount: assets_fs
resources:
adapter: resource_adapter
mount: resources_fs

@ -9,7 +9,17 @@ vich_uploader:
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
directory_namer:
service: Vich\UploaderBundle\Naming\SubdirDirectoryNamer
options: {chars_per_dir: 1, dirs: 3}
options: {chars_per_dir: 1, dirs: 3} # will create directory "a/b/c" for "abcdef.jpg"
inject_on_load: true
delete_on_update: true
delete_on_remove: true
assets:
uri_prefix: ''
upload_destination: assets_fs
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
directory_namer:
service: Chamilo\CoreBundle\Component\Utils\AssetDirectoryNamer
options: {property: 'category'}
inject_on_load: true
delete_on_update: true
delete_on_remove: true

@ -44,6 +44,8 @@ services:
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
Chamilo\CoreBundle\Component\Utils\AssetDirectoryNamer:
Chamilo\CoreBundle\Component\Utils\Glide:
arguments:
- {source: '@oneup_flysystem.resources_filesystem', cache: '@oneup_flysystem.cache_resources_filesystem'}

@ -0,0 +1,78 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Component\Utils;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Vich\UploaderBundle\Mapping\PropertyMapping;
use Vich\UploaderBundle\Naming\ConfigurableInterface;
use Vich\UploaderBundle\Naming\DirectoryNamerInterface;
use Vich\UploaderBundle\Util\Transliterator;
class AssetDirectoryNamer implements DirectoryNamerInterface, ConfigurableInterface
{
/**
* @var string
*/
private $propertyPath;
private $charsPerDir = 2;
private $dirs = 1;
/**
* @var bool
*/
private $transliterate = false;
/**
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
/**
* @var Transliterator
*/
private $transliterator;
public function __construct(?PropertyAccessorInterface $propertyAccessor, Transliterator $transliterator)
{
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
$this->transliterator = $transliterator;
}
/**
* @param array $options Options for this namer. The following options are accepted:
* - chars_per_dir: how many chars use for each dir.
* - dirs: how many dirs create
*/
public function configure(array $options): void
{
if (empty($options['property'])) {
throw new \InvalidArgumentException('Option "property" is missing or empty.');
}
$this->propertyPath = $options['property'];
$options = \array_merge(['chars_per_dir' => $this->charsPerDir, 'dirs' => $this->dirs], $options);
$this->charsPerDir = $options['chars_per_dir'];
$this->dirs = $options['dirs'];
}
public function directoryName($object, PropertyMapping $mapping): string
{
$fileName = $mapping->getFileName($object);
$category = $this->propertyAccessor->getValue($object, $this->propertyPath);
$parts[] = $category;
/*for ($i = 0, $start = 0; $i < $this->dirs; $i++, $start += $this->charsPerDir) {
$parts[] = \substr($fileName, $start, $this->charsPerDir);
}*/
$parts[] = $fileName;
return \implode('/', $parts);
}
}

@ -0,0 +1,383 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* @ORM\Entity
* @Vich\Uploadable
*
* @ORM\Table(name="asset")
*/
class Asset
{
use TimestampableEntity;
public const SCORM = 'scorm';
public const WATERMARK = 'watermark';
public const CSS = 'css';
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var string
*
* @Assert\NotBlank()
*
* @ORM\Column(type="string", length=255)
*/
protected $title;
/**
* @var string
*
* @Assert\Choice({Asset::SCORM, Asset::WATERMARK, Asset::CSS}, message="Choose a valid category.")
*
* @ORM\Column(type="string", length=255)
*/
protected $category;
/**
* @var File
*
* @Assert\NotNull()
* @Vich\UploadableField(
* mapping="assets",
* fileNameProperty="title",
* size="size",
* mimeType="mimeType",
* originalName="originalName",
* dimensions="dimensions"
* )
*/
protected $file;
/**
* @ORM\Column(type="boolean")
*/
protected bool $compressed;
/**
* @var string
*
* @Groups({"resource_file:read", "resource_node:read", "document:read"})
* @ORM\Column(type="text", nullable=true)
*/
protected $mimeType;
/**
* @var string
*
* @ORM\Column(type="text", nullable=true)
*/
protected $originalName;
/**
* @var string
*
* @Groups({"resource_file:read", "resource_node:read", "document:read"})
* @ORM\Column(type="simple_array", nullable=true)
*/
protected $dimensions;
/**
* @var int
*
* @ORM\Column(type="integer")
*/
protected $size;
/**
* @var string
*
* @ORM\Column(name="crop", type="string", length=255, nullable=true)
*/
protected $crop;
/**
* @var array
*
* @ORM\Column(type="array", nullable=true)
*/
protected $metadata;
/**
* @var string
*
* @ORM\Column(name="description", type="text", nullable=true)
*/
protected $description;
public function __construct()
{
$this->metadata = [];
$this->size = 0;
$this->compressed = false;
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
public function __toString(): string
{
return $this->getOriginalName();
}
public function isImage(): bool
{
$mimeType = $this->getMimeType();
if (false !== strpos($mimeType, 'image')) {
return true;
}
return false;
}
public function isVideo(): bool
{
$mimeType = $this->getMimeType();
if (false !== strpos($mimeType, 'video')) {
return true;
}
return false;
}
/**
* @return string
*/
public function getCrop()
{
return $this->crop;
}
/**
* @param string $crop
*
* @return $this
*/
public function setCrop($crop): self
{
$this->crop = $crop;
return $this;
}
public function getSize(): int
{
return (int) $this->size;
}
/**
* @param int $size
*/
public function setSize($size): self
{
$this->size = $size;
return $this;
}
/*public function getDescription(): string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}*/
public function getMimeType(): string
{
return $this->mimeType;
}
/**
* @param string $mimeType
*/
public function setMimeType($mimeType): self
{
$this->mimeType = $mimeType;
return $this;
}
public function getOriginalName(): string
{
return (string) $this->originalName;
}
/**
* @param string $originalName
*/
public function setOriginalName($originalName): self
{
$this->originalName = $originalName;
return $this;
}
public function getDimensions(): array
{
return $this->dimensions;
}
/**
* @param $dimensions
*/
public function setDimensions($dimensions): self
{
$this->dimensions = $dimensions;
return $this;
}
public function getWidth(): int
{
$data = $this->getDimensions();
if ($data) {
//$data = explode(',', $data);
return (int) $data[0];
}
return 0;
}
public function getHeight(): int
{
$data = $this->getDimensions();
if ($data) {
//$data = explode(',', $data);
return (int) $data[1];
}
return 0;
}
public function getMetadata(): array
{
return $this->metadata;
}
public function setMetadata(array $metadata): self
{
$this->metadata = $metadata;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getFile(): ?File
{
return $this->file;
}
public function hasFile()
{
return null !== $this->file;
}
/**
* @param File|UploadedFile $file
*/
public function setFile(File $file = null): self
{
$this->file = $file;
if (null !== $file) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*
* @return Asset
*/
public function setTitle(string $title): Asset
{
$this->title = $title;
return $this;
}
/**
* @return string
*/
public function getCategory(): string
{
return $this->category;
}
/**
* @param string $category
*
* @return Asset
*/
public function setCategory(string $category): Asset
{
$this->category = $category;
return $this;
}
public function getCompressed(): bool
{
return $this->compressed;
}
public function setCompressed(bool $compressed)
{
$this->compressed = $compressed;
return $this;
}
}

@ -6,6 +6,7 @@ namespace Chamilo\CoreBundle\Framework;
use Chamilo\CoreBundle\Component\Editor\Editor;
use Chamilo\CoreBundle\Manager\SettingsManager;
use Chamilo\CoreBundle\Repository\AssetRepository;
use Chamilo\CoreBundle\Repository\CourseCategoryRepository;
use Chamilo\CoreBundle\Repository\Node\AccessUrlRepository;
use Chamilo\CoreBundle\Repository\Node\CourseRepository;
@ -52,8 +53,10 @@ use Chamilo\CourseBundle\Repository\CThematicPlanRepository;
use Chamilo\CourseBundle\Repository\CThematicRepository;
use Chamilo\CourseBundle\Repository\CWikiRepository;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\FormFactory;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
@ -132,18 +135,12 @@ class Container
return self::$container->get('security.role_hierarchy');
}
/**
* @return string
*/
public static function getLogDir()
public static function getLogDir(): string
{
return self::$container->get('kernel')->getLogDir();
}
/**
* @return string
*/
public static function getCacheDir()
public static function getCacheDir(): string
{
return self::$container->get('kernel')->getCacheDir().'/';
}
@ -260,9 +257,9 @@ class Container
return false;
}
public static function getMailer()
public static function getMailer(): Mailer
{
return self::$container->get('Symfony\Component\Mailer\Mailer');
return self::$container->get(Mailer::class);
}
public static function getSettingsManager(): SettingsManager
@ -283,7 +280,7 @@ class Container
return \Database::getManager();
}
public static function getUserManager()
public static function getUserManager(): UserRepository
{
return self::$container->get(UserRepository::class);
}
@ -508,10 +505,7 @@ class Container
return self::$container->get(CWikiRepository::class);
}
/**
* @return \Symfony\Component\Form\FormFactory
*/
public static function getFormFactory()
public static function getFormFactory(): FormFactory
{
return self::$container->get('form.factory');
}
@ -526,27 +520,22 @@ class Container
$session->getFlashBag()->add($type, $message);
}
/**
* @return Router
*/
public static function getRouter()
public static function getRouter(): Router
{
return self::$container->get('router');
}
/**
* @return ToolChain
*/
public static function getToolChain()
public static function getToolChain(): ToolChain
{
return self::$container->get(ToolChain::class);
}
/**
* @param ContainerInterface $container
* @param bool $setSession
*/
public static function setLegacyServices($container, $setSession = true)
public static function getAssetRepository(): AssetRepository
{
return self::$container->get(AssetRepository::class);
}
public static function setLegacyServices(ContainerInterface $container, bool $setSession = true)
{
\Database::setConnection($container->get('doctrine.dbal.default_connection'));
$em = $container->get('doctrine.orm.entity_manager');

@ -0,0 +1,103 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\Asset;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\CourseCategory;
use Chamilo\CoreBundle\Entity\ResourceFile;
use Chamilo\CoreBundle\Entity\ResourceLink;
use Chamilo\CoreBundle\Entity\ResourceNode;
use Chamilo\CoreBundle\Entity\ResourceType;
use Chamilo\CoreBundle\Entity\Session;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use League\Flysystem\FilesystemInterface;
use League\Flysystem\MountManager;
use League\Flysystem\ZipArchive\ZipArchiveAdapter;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Vich\UploaderBundle\Storage\FlysystemStorage;
class AssetRepository extends ServiceEntityRepository
{
protected $mountManager;
protected $storage;
public function __construct(ManagerRegistry $registry, FlysystemStorage $storage, MountManager $mountManager)
{
parent::__construct($registry, Asset::class);
$this->storage = $storage;
$this->mountManager = $mountManager;
}
public function getFilename(ResourceFile $resourceFile)
{
return $this->storage->resolveUri($resourceFile);
}
/**
* @return FilesystemInterface
*/
public function getFileSystem()
{
// Flysystem mount name is saved in config/packages/oneup_flysystem.yaml
return $this->mountManager->getFilesystem('assets_fs');
}
public function unZipFile(Asset $asset, ZipArchiveAdapter $zipArchiveAdapter)
{
$folder = '/'.$asset->getCategory().'/'.$asset->getTitle();
$fs = $this->getFileSystem();
if ($fs->has($folder)) {
$contents = $zipArchiveAdapter->listContents();
foreach ($contents as $data) {
if ($fs->has($folder.'/'.$data['path'])) {
continue;
}
if ('dir' === $data['type']) {
$fs->createDir($folder.'/'.$data['path']);
continue;
}
$fs->write($folder.'/'.$data['path'], $zipArchiveAdapter->read($data['path']));
}
}
}
public function getFileContent(Asset $asset): string
{
try {
if ($asset->hasFile()) {
$file = $asset->getFile();
$fileName = $this->getFilename($file);
return $this->getFileSystem()->read($fileName);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($asset);
}
}
public function getFileStream(Asset $asset)
{
try {
if ($asset->hasFile()) {
$file = $asset->getFile();
$fileName = $this->getFilename($file);
return $this->getFileSystem()->readStream($fileName);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($asset);
}
}
}
Loading…
Cancel
Save