PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / NewsletterTemplates / ThumbnailSaver.php
mailpoet / lib / NewsletterTemplates Last commit date
BrandStyles.php 3 years ago NewsletterTemplatesRepository.php 2 months ago TemplateImageLoader.php 2 years ago ThumbnailSaver.php 2 months ago index.php 3 years ago
ThumbnailSaver.php
147 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\NewsletterTemplates;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Config\Env;
9 use MailPoet\Entities\NewsletterTemplateEntity;
10 use MailPoet\Util\Security;
11 use MailPoet\WP\Functions as WPFunctions;
12
13 class ThumbnailSaver {
14 const THUMBNAIL_DIRECTORY = 'newsletter_thumbnails';
15 const IMAGE_QUALITY = 80;
16
17 /** @var NewsletterTemplatesRepository */
18 private $repository;
19
20 /** @var WPFunctions */
21 private $wp;
22
23 /** @var string */
24 private $baseDirectory;
25
26 /** @var string */
27 private $baseUrl;
28
29 public function __construct(
30 NewsletterTemplatesRepository $repository,
31 WPFunctions $wp
32 ) {
33 $this->repository = $repository;
34 $this->wp = $wp;
35 $this->baseDirectory = Env::$tempPath;
36 $this->baseUrl = Env::$tempUrl;
37 }
38
39 public function ensureTemplateThumbnailsForAll() {
40 $templateIds = $this->repository->getIdsOfEditableTemplates();
41 foreach ($templateIds as $templateId) {
42 $template = $this->repository->findOneById((int)$templateId);
43 if (!$template) continue;
44 $this->ensureTemplateThumbnailFile($template);
45 // Remove template entity from memory after it was processed
46 $this->repository->detach($template);
47 unset($template);
48 }
49 }
50
51 public function ensureTemplateThumbnailFile(NewsletterTemplateEntity $template): NewsletterTemplateEntity {
52 if ($template->getReadonly()) {
53 return $template;
54 }
55 $thumbnailUrl = $template->getThumbnail();
56 $savedFilename = null;
57 $savedBaseUrl = null;
58 if ($thumbnailUrl && strpos($thumbnailUrl, self::THUMBNAIL_DIRECTORY) !== false) {
59 [$savedBaseUrl, $savedFilename] = explode('/' . self::THUMBNAIL_DIRECTORY . '/', $thumbnailUrl);
60 }
61 $file = $this->baseDirectory . '/' . self::THUMBNAIL_DIRECTORY . '/' . $savedFilename;
62 if (!$savedFilename || !file_exists($file)) {
63 $this->saveTemplateImage($template);
64 }
65
66 // File might exist but domain was changed
67 $thumbnailUrl = $template->getThumbnail();
68 if ($savedBaseUrl && $savedBaseUrl !== $this->baseUrl && $thumbnailUrl) {
69 $template->setThumbnail(str_replace($savedBaseUrl, $this->baseUrl, $thumbnailUrl));
70 }
71 return $template;
72 }
73
74 private function saveTemplateImage(NewsletterTemplateEntity $template): void {
75 $data = $template->getThumbnailData();
76 if (!$data) {
77 return;
78 }
79 // Check that data contains Base 64 encoded jpeg
80 if (strpos($data, 'data:image/jpeg;base64') !== 0) {
81 return;
82 }
83 $thumbNailsDirectory = $this->baseDirectory . '/' . self::THUMBNAIL_DIRECTORY;
84 if (!file_exists($thumbNailsDirectory)) {
85 $this->wp->wpMkdirP($thumbNailsDirectory);
86 }
87 $file = $thumbNailsDirectory . '/' . Security::generateHash(16) . '_template_' . $template->getId() . '.jpg';
88
89 // Save the original quality image to a file and update DB record
90 if (!$this->saveBase64AsImageFile($file, $data)) {
91 return;
92 }
93 $url = str_replace($this->baseDirectory, $this->baseUrl, $file);
94 $template->setThumbnail($url);
95 $this->repository->flush();
96
97 // It is important that compression happens after the url was saved to DB.
98 // For some large files there is a risk that compression (if done using GD library) may fail due hitting memory limit.
99 // This way if the error occures the url is already saved and next time (e.g. next cron run) the image will be skipped
100 // and the previously saved original quality image used
101 $this->compressImage($file);
102 }
103
104 private function compressImage(string $file): bool {
105 $editor = $this->wp->wpGetImageEditor($file);
106 if ($editor instanceof \WP_Error) {
107 return false;
108 }
109 $result = $editor->set_quality(self::IMAGE_QUALITY);
110 if ($result instanceof \WP_Error) {
111 return false;
112 }
113 $result = $editor->save($file);
114 if ($result instanceof \WP_Error) {
115 return false;
116 }
117 unset($editor);
118 return true;
119 }
120
121 /**
122 * Saves base64 to a file without any compression. We decode the base64 data, because encoded data
123 * is considered URL, and file_put_contents() would fail when allow_url_fopen is disabled in PHP.
124 *
125 * @param string $file The path to the file to save the base64 data to.
126 * @param string $data The base64 data to save to the file.
127 *
128 * @return bool
129 */
130 private function saveBase64AsImageFile(string $file, string $data): bool {
131 // Extract the base64 data from the data URI
132 if (preg_match('/^data:.*;base64,(.+)$/', $data, $matches)) {
133 $base64Data = $matches[1];
134 } else {
135 // If it's not a proper data URI, assume it's already base64 encoded
136 $base64Data = $data;
137 }
138
139 $decodedData = base64_decode($base64Data, true);
140 if ($decodedData === false) {
141 return false;
142 }
143
144 return file_put_contents($file, $decodedData) !== false;
145 }
146 }
147