3 namespace Wallabag\CoreBundle\Helper
;
5 use Psr\Log\LoggerInterface
;
6 use Symfony\Component\DomCrawler\Crawler
;
8 use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser
;
9 use Symfony\Component\Finder\Finder
;
13 const REGENERATE_PICTURES_QUALITY
= 80;
21 public function __construct(Client
$client, $baseFolder, $wallabagUrl, LoggerInterface
$logger)
23 $this->client
= $client;
24 $this->baseFolder
= $baseFolder;
25 $this->wallabagUrl
= rtrim($wallabagUrl, '/');
26 $this->logger
= $logger;
27 $this->mimeGuesser
= new MimeTypeExtensionGuesser();
33 * Setup base folder where all images are going to be saved.
35 private function setFolder()
37 // if folder doesn't exist, attempt to create one and store the folder name in property $folder
38 if (!file_exists($this->baseFolder
)) {
39 mkdir($this->baseFolder
, 0755, true);
44 * Process the html and extract image from it, save them to local and return the updated html.
46 * @param int $entryId ID of the entry
48 * @param string $url Used as a base path for relative image and folder
52 public function processHtml($entryId, $html, $url)
54 $crawler = new Crawler($html);
56 ->filterXpath('//img')
57 ->extract(array('src'));
59 $relativePath = $this->getRelativePath($entryId);
61 // download and save the image to the folder
62 foreach ($result as $image) {
63 $imagePath = $this->processSingleImage($entryId, $image, $url, $relativePath);
65 if (false === $imagePath) {
69 $html = str_replace($image, $imagePath, $html);
76 * Process a single image:
78 * - re-saved it (for security reason)
79 * - return the new local path.
81 * @param int $entryId ID of the entry
82 * @param string $imagePath Path to the image to retrieve
83 * @param string $url Url from where the image were found
84 * @param string $relativePath Relative local path to saved the image
86 * @return string Relative url to access the image from the web
88 public function processSingleImage($entryId, $imagePath, $url, $relativePath = null)
90 if (null === $relativePath) {
91 $relativePath = $this->getRelativePath($entryId);
94 $this->logger
->debug('DownloadImages: working on image: '.$imagePath);
96 $folderPath = $this->baseFolder
.'/'.$relativePath;
99 $absolutePath = $this->getAbsoluteLink($url, $imagePath);
100 if (false === $absolutePath) {
101 $this->logger
->error('DownloadImages: Can not determine the absolute path for that image, skipping.');
107 $res = $this->client
->get($absolutePath);
108 } catch (\Exception
$e) {
109 $this->logger
->error('DownloadImages: Can not retrieve image, skipping.', ['exception' => $e]);
114 $ext = $this->mimeGuesser
->guess($res->getHeader('content-type'));
115 $this->logger
->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]);
116 if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) {
117 $this->logger
->error('DownloadImages: Processed image with not allowed extension. Skipping '.$imagePath);
121 $hashImage = hash('crc32', $absolutePath);
122 $localPath = $folderPath.'/'.$hashImage.'.'.$ext;
125 $im = imagecreatefromstring($res->getBody());
126 } catch (\Exception
$e) {
131 $this->logger
->error('DownloadImages: Error while regenerating image', ['path' => $localPath]);
138 imagegif($im, $localPath);
139 $this->logger
->debug('DownloadImages: Re-creating gif');
143 imagejpeg($im, $localPath, self
::REGENERATE_PICTURES_QUALITY
);
144 $this->logger
->debug('DownloadImages: Re-creating jpg');
147 imagepng($im, $localPath, ceil(self
::REGENERATE_PICTURES_QUALITY
/ 100 * 9));
148 $this->logger
->debug('DownloadImages: Re-creating png');
153 return $this->wallabagUrl
.'/assets/images/'.$relativePath.'/'.$hashImage.'.'.$ext;
157 * Remove all images for the given entry id.
159 * @param int $entryId ID of the entry
161 public function removeImages($entryId)
163 $relativePath = $this->getRelativePath($entryId);
164 $folderPath = $this->baseFolder
.'/'.$relativePath;
166 $finder = new Finder();
169 ->ignoreDotFiles(true)
172 foreach ($finder as $file) {
173 @unlink($file->getRealPath());
180 * Generate the folder where we are going to save images based on the entry url.
182 * @param int $entryId ID of the entry
186 private function getRelativePath($entryId)
188 $hashId = hash('crc32', $entryId);
189 $relativePath = $hashId[0].'/'.$hashId[1].'/'.$hashId;
190 $folderPath = $this->baseFolder
.'/'.$relativePath;
192 if (!file_exists($folderPath)) {
193 mkdir($folderPath, 0777, true);
196 $this->logger
->debug('DownloadImages: Folder used for that Entry id', ['folder' => $folderPath, 'entryId' => $entryId]);
198 return $relativePath;
202 * Make an $url absolute based on the $base.
204 * @see Graby->makeAbsoluteStr
206 * @param string $base Base url
207 * @param string $url Url to make it absolute
209 * @return false|string
211 private function getAbsoluteLink($base, $url)
213 if (preg_match('!^https?://!i', $url)) {
218 $base = new \
SimplePie_IRI($base);
220 // remove '//' in URL path (causes URLs not to resolve properly)
221 if (isset($base->ipath
)) {
222 $base->ipath
= preg_replace('!//+!', '/', $base->ipath
);
225 if ($absolute = \SimplePie_IRI
::absolutize($base, $url)) {
226 return $absolute->get_uri();
229 $this->logger
->error('DownloadImages: Can not make an absolute link', ['base' => $base, 'url' => $url]);