PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 All 128 releases
← All changes | app/Draft/Controllers/ImageController.php +99 -0 3.0.5 → 3.2.2 View file →
@@ -23,8 +23,16 @@
23 23
24 24 class ImageController
25 25 {
26 26 /**
27 + * Must match `PILL_RIGHT_EDGE` in `src/Shared/lib/ai-label.js`.
28 + *
29 + * @var float
30 + */
31 + // phpcs:ignore PSR12.Properties.ConstantVisibility.NotFound -- 7.0 floor: no const visibility
32 + const PILL_RIGHT_EDGE = 0.85;
33 +
34 + /**
27 35 * Upload the provided image
28 36 *
29 37 * @param \WP_REST_Request $request - The request.
30 38 * @return \WP_REST_Response
@@ -36,10 +44,34 @@
36 44 require_once ABSPATH . 'wp-admin/includes/file.php';
37 45 require_once ABSPATH . 'wp-admin/includes/image.php';
38 46 }
39 47
48 + // media_sideload_image() takes no file name; it names the file from the URL.
49 + $markAiFilename = function ($file) {
50 + $info = pathinfo($file['name']);
51 + $file['name'] = "{$info['filename']}-ai-generated.{$info['extension']}";
52 + return $file;
53 + };
54 + if ($request->get_param('ai_generated')) {
55 + add_filter('wp_handle_sideload_prefilter', $markAiFilename);
56 + }
57 +
40 58 $imageId = \media_sideload_image($request->get_param('source'), 0, null, 'id');
59 + remove_filter('wp_handle_sideload_prefilter', $markAiFilename);
41 60
61 + // Without this the caller can't tell a failed upload from a declined one.
62 + if (\is_wp_error($imageId)) {
63 + return new \WP_REST_Response(['message' => $imageId->get_error_message()], 500);
64 + }
65 +
66 + if ($request->get_param('ai_generated')) {
67 + update_post_meta($imageId, 'extendify_ai_generated', true);
68 + $label = $request->get_file_params()['disclosure_label'] ?? null;
69 + if (isset($label['tmp_name'])) {
70 + self::stampImage($imageId, $label['tmp_name']);
71 + }
72 + }
73 +
42 74 if ($request->get_param('alt_text')) {
43 75 update_post_meta(
44 76 $imageId,
45 77 '_wp_attachment_image_alt',
@@ -68,6 +100,73 @@
68 100 'source_url' => wp_get_attachment_url($imageId),
69 101 'alt_text' => $altText,
70 102 ]
71 103 );
104 + }
105 +
106 + /**
107 + * Composite the client-rendered disclosure label onto the attachment. The
108 + * PNG already includes its border, unlike the canvas path's fill box.
109 + *
110 + * @param int $imageId - The attachment ID.
111 + * @param string $labelPath - Path to the uploaded label PNG.
112 + * @return void
113 + */
114 + private static function stampImage($imageId, $labelPath)
115 + {
116 + $file = \get_attached_file($imageId);
117 + $type = $file ? (\wp_check_filetype($file)['type'] ?? null) : null;
118 + $loaders = [
119 + 'image/jpeg' => 'imagecreatefromjpeg',
120 + 'image/png' => 'imagecreatefrompng',
121 + 'image/webp' => 'imagecreatefromwebp',
122 + ];
123 + if (!isset($loaders[$type]) || !function_exists($loaders[$type]) || !is_file($file)) {
124 + return;
125 + }
126 +
127 + // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
128 + $image = @call_user_func($loaders[$type], $file);
129 + // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
130 + $label = @imagecreatefrompng($labelPath);
131 + if (!$image || !$label) {
132 + return;
133 + }
134 +
135 + $width = imagesx($image);
136 + $height = imagesy($image);
137 + // The client renders the label at a 48px font; the canvas stamp uses 2% of the shorter side.
138 + $scale = max(12, round(min($width, $height) * 0.02)) / 48;
139 + $labelWidth = (int) round(imagesx($label) * $scale);
140 + $labelHeight = (int) round(imagesy($label) * $scale);
141 + imagealphablending($image, true);
142 + imagecopyresampled(
143 + $image,
144 + $label,
145 + (int) round(($width * self::PILL_RIGHT_EDGE) - $labelWidth),
146 + (int) round(($height - $labelHeight) / 2),
147 + 0,
148 + 0,
149 + $labelWidth,
150 + $labelHeight,
151 + imagesx($label),
152 + imagesy($label)
153 + );
154 +
155 + if ($type === 'image/jpeg') {
156 + imagejpeg($image, $file, 90);
157 + }
158 +
159 + if ($type === 'image/png') {
160 + imagesavealpha($image, true);
161 + imagepng($image, $file);
162 + }
163 +
164 + if ($type === 'image/webp') {
165 + imagewebp($image, $file, 90);
166 + }
167 +
168 + // Sub-sizes were generated from the unstamped original; rebuild them.
169 + $metadata = \wp_generate_attachment_metadata($imageId, $file);
170 + \wp_update_attachment_metadata($imageId, $metadata);
72 171 }
73 172 }