vendor/roothirsch/dam-bundle/Entity/File.php line 20

Open in your IDE?
  1. <?php
  2. namespace Roothirsch\DamBundle\Entity;
  3. use Roothirsch\CoreBundle\Translation\Entity\Language;
  4. use Roothirsch\DamBundle\Repository\FileRepository;
  5. use Roothirsch\CoreBundle\Entity\Traits\TimetrackedTrait;
  6. use Doctrine\ORM\Mapping as ORM;
  7. use \Roothirsch\DamBundle\Entity\Asset;
  8. use ApiPlatform\Core\Annotation\ApiResource;
  9. use Symfony\Component\Finder\SplFileInfo;
  10. use Symfony\Component\String\UnicodeString;
  11. /**
  12. * @ORM\Entity(repositoryClass=FileRepository::class)
  13. * @ORM\Table(name="dam_file")
  14. * @ApiResource( shortName="Dam/File")
  15. * @ORM\HasLifecycleCallbacks()
  16. */
  17. class File
  18. {
  19. /**
  20. * @ORM\Id
  21. * @ORM\GeneratedValue
  22. * @ORM\Column(type="integer")
  23. */
  24. private $id;
  25. /**
  26. * @ORM\Column(type="string", length=255)
  27. */
  28. private $name;
  29. /**
  30. * @ORM\Column(type="string", length=255)
  31. */
  32. private $title;
  33. /**
  34. * @ORM\Column(type="text")
  35. */
  36. private $contentSummary = '';
  37. /**
  38. * @ORM\Column(type="text")
  39. */
  40. private $filepath;
  41. /**
  42. */
  43. private $oldFilepath;
  44. /**
  45. * @ORM\Column(type="integer")
  46. */
  47. private $size;
  48. /**
  49. * @ORM\Column(type="string", length=255)
  50. */
  51. private $extension;
  52. /**
  53. * @var \DateTime $created
  54. * @ORM\Column(type="datetime")
  55. */
  56. private $createdAt;
  57. /**
  58. * @var \DateTime $updated
  59. * @ORM\Column(type="datetime")
  60. */
  61. private $updatedAt;
  62. /**
  63. * @ORM\ManyToOne(targetEntity=Asset::class, inversedBy="files")
  64. * @ORM\JoinColumn(nullable=false)
  65. */
  66. private $asset;
  67. /**
  68. * @ORM\ManyToOne(targetEntity=Language::class)
  69. */
  70. private $language;
  71. public function __construct(SplFileInfo $fileInfo = null)
  72. {
  73. if ($fileInfo instanceof \SplFileInfo) {
  74. $this->name = $fileInfo->getFilename();
  75. $this->title = str_replace('_', ' ', $fileInfo->getFilenameWithoutExtension());
  76. $this->filepath = $fileInfo->getRelativePathname();
  77. $this->size = $fileInfo->getSize();
  78. $this->extension = $fileInfo->getExtension();
  79. $this->createdAt = new \DateTime('@' . $fileInfo->getMTime());
  80. $this->updatedAt = new \DateTime('@' . $fileInfo->getCTime());
  81. }
  82. }
  83. public function __toString()
  84. {
  85. return strval($this->name);
  86. }
  87. /**
  88. * @ORM\PostLoad
  89. */
  90. public function cacheFilepath()
  91. {
  92. $this->oldFilepath = $this->filepath;
  93. }
  94. /**
  95. * @ORM\PrePersist
  96. * @ORM\PreUpdate
  97. */
  98. public function updateFileInformation($copyInsteadOfRename = false)
  99. {
  100. if (!file_exists(getcwd() . '/uploads/dam/' . $this->filepath)) {
  101. return;
  102. }
  103. $fileInfo = new SplFileInfo(
  104. getcwd() . '/uploads/dam/' . $this->filepath,
  105. getcwd() . '/uploads/dam/',
  106. $this->filepath
  107. );
  108. $this->name = $fileInfo->getFilename();
  109. $this->title = str_replace('_', ' ', $fileInfo->getFilenameWithoutExtension());
  110. $this->size = $fileInfo->getSize();
  111. $this->extension = $fileInfo->getExtension();
  112. $this->createdAt = new \DateTime('@' . $fileInfo->getMTime());
  113. $updatedAt = new \DateTime('@' . $fileInfo->getCTime());
  114. if ($updatedAt > $this->updatedAt || empty($this->contentSummary)) {
  115. // For PDFs, just set placeholder - async processing will be handled by FileContentSummaryListener
  116. if ($this->extension === 'pdf') {
  117. $this->contentSummary = '[Processing...]';
  118. }
  119. }
  120. $this->updatedAt = $updatedAt;
  121. }
  122. public function getId(): ?int
  123. {
  124. return $this->id;
  125. }
  126. public function getName(): ?string
  127. {
  128. return $this->name;
  129. }
  130. public function setName(string $name): self
  131. {
  132. $this->name = $name;
  133. return $this;
  134. }
  135. public function getTitle(): ?string
  136. {
  137. return $this->title;
  138. }
  139. public function setTitle(string $title): self
  140. {
  141. $this->title = $title;
  142. return $this;
  143. }
  144. public function getFilepath(): ?string
  145. {
  146. return $this->filepath;
  147. }
  148. public function setFilepath($filepath): self
  149. {
  150. $this->filepath = $filepath;
  151. return $this;
  152. }
  153. public function getSize(): ?int
  154. {
  155. return $this->size;
  156. }
  157. public function setSize(int $size): self
  158. {
  159. $this->size = $size;
  160. return $this;
  161. }
  162. /**
  163. * @return string
  164. */
  165. public function getExtension(): string
  166. {
  167. return $this->extension;
  168. }
  169. /**
  170. * @param string $extension
  171. */
  172. public function setExtension(string $extension): void
  173. {
  174. $this->extension = $extension;
  175. }
  176. public function getAsset(): ?Asset
  177. {
  178. return $this->asset;
  179. }
  180. public function setAsset(?Asset $asset): self
  181. {
  182. $this->asset = $asset;
  183. return $this;
  184. }
  185. /**
  186. * @return \DateTime
  187. */
  188. public function getCreatedAt(): \DateTime
  189. {
  190. return $this->createdAt;
  191. }
  192. /**
  193. * @param \DateTime $createdAt
  194. */
  195. public function setCreatedAt(\DateTime $createdAt): void
  196. {
  197. $this->createdAt = $createdAt;
  198. }
  199. /**
  200. * @return \DateTime
  201. */
  202. public function getUpdatedAt(): \DateTime
  203. {
  204. return $this->updatedAt;
  205. }
  206. /**
  207. * @param \DateTime $updatedAt
  208. */
  209. public function setUpdatedAt(\DateTime $updatedAt): void
  210. {
  211. $this->updatedAt = $updatedAt;
  212. }
  213. /**
  214. * @return mixed
  215. */
  216. public function getLanguage()
  217. {
  218. return $this->language;
  219. }
  220. /**
  221. * @param mixed $language
  222. */
  223. public function setLanguage($language): void
  224. {
  225. $this->language = $language;
  226. }
  227. /**
  228. * @return mixed
  229. */
  230. public function getContentSummary()
  231. {
  232. return $this->contentSummary;
  233. }
  234. /**
  235. * @param mixed $contentSummary
  236. */
  237. public function setContentSummary($contentSummary): void
  238. {
  239. $this->contentSummary = $this->sanitizeUtf8Text($contentSummary);
  240. }
  241. /**
  242. * Sanitize text to ensure UTF-8 compliance and remove problematic characters
  243. */
  244. private function sanitizeUtf8Text(?string $text): string
  245. {
  246. if (empty($text)) {
  247. return '';
  248. }
  249. try {
  250. // Use Symfony String component for robust UTF-8 handling
  251. $string = new UnicodeString($text);
  252. // Remove control characters except newlines, tabs and carriage returns
  253. $cleaned = $string
  254. ->ascii() // Convert to ASCII-safe representation
  255. ->trim() // Remove leading/trailing whitespace
  256. ->toString();
  257. // Limit length to prevent extremely large summaries
  258. if (strlen($cleaned) > 65535) {
  259. $cleaned = substr($cleaned, 0, 65535) . '...';
  260. }
  261. return $cleaned;
  262. } catch (\Throwable $e) {
  263. // Fallback: remove problematic characters manually
  264. $fallback = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]/', '', $text ?? '');
  265. return substr(trim($fallback), 0, 65535);
  266. }
  267. }
  268. }