PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
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 0.6.0 All 127 releases
← All changes | app/Draft/Controllers/ImageController.php +88 -0 3.0.63.2.1 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
@@ -38,8 +46,21 @@
38 46 }
39 47
40 48 $imageId = \media_sideload_image($request->get_param('source'), 0, null, 'id');
41 49
50 + // Without this the caller can't tell a failed upload from a declined one.
51 + if (\is_wp_error($imageId)) {
52 + return new \WP_REST_Response(['message' => $imageId->get_error_message()], 500);
53 + }
54 +
55 + if ($request->get_param('ai_generated')) {
56 + update_post_meta($imageId, 'extendify_ai_generated', true);
57 + $label = $request->get_file_params()['disclosure_label'] ?? null;
58 + if (isset($label['tmp_name'])) {
59 + self::stampImage($imageId, $label['tmp_name']);
60 + }
61 + }
62 +
42 63 if ($request->get_param('alt_text')) {
43 64 update_post_meta(
44 65 $imageId,
45 66 '_wp_attachment_image_alt',
@@ -68,6 +89,73 @@
68 89 'source_url' => wp_get_attachment_url($imageId),
69 90 'alt_text' => $altText,
70 91 ]
71 92 );
93 + }
94 +
95 + /**
96 + * Composite the client-rendered disclosure label onto the attachment. The
97 + * PNG already includes its border, unlike the canvas path's fill box.
98 + *
99 + * @param int $imageId - The attachment ID.
100 + * @param string $labelPath - Path to the uploaded label PNG.
101 + * @return void
102 + */
103 + private static function stampImage($imageId, $labelPath)
104 + {
105 + $file = \get_attached_file($imageId);
106 + $type = $file ? (\wp_check_filetype($file)['type'] ?? null) : null;
107 + $loaders = [
108 + 'image/jpeg' => 'imagecreatefromjpeg',
109 + 'image/png' => 'imagecreatefrompng',
110 + 'image/webp' => 'imagecreatefromwebp',
111 + ];
112 + if (!isset($loaders[$type]) || !function_exists($loaders[$type]) || !is_file($file)) {
113 + return;
114 + }
115 +
116 + // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
117 + $image = @call_user_func($loaders[$type], $file);
118 + // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
119 + $label = @imagecreatefrompng($labelPath);
120 + if (!$image || !$label) {
121 + return;
122 + }
123 +
124 + $width = imagesx($image);
125 + $height = imagesy($image);
126 + // The client renders the label at a 48px font; the canvas stamp uses 2% of the shorter side.
127 + $scale = max(12, round(min($width, $height) * 0.02)) / 48;
128 + $labelWidth = (int) round(imagesx($label) * $scale);
129 + $labelHeight = (int) round(imagesy($label) * $scale);
130 + imagealphablending($image, true);
131 + imagecopyresampled(
132 + $image,
133 + $label,
134 + (int) round(($width * self::PILL_RIGHT_EDGE) - $labelWidth),
135 + (int) round(($height - $labelHeight) / 2),
136 + 0,
137 + 0,
138 + $labelWidth,
139 + $labelHeight,
140 + imagesx($label),
141 + imagesy($label)
142 + );
143 +
144 + if ($type === 'image/jpeg') {
145 + imagejpeg($image, $file, 90);
146 + }
147 +
148 + if ($type === 'image/png') {
149 + imagesavealpha($image, true);
150 + imagepng($image, $file);
151 + }
152 +
153 + if ($type === 'image/webp') {
154 + imagewebp($image, $file, 90);
155 + }
156 +
157 + // Sub-sizes were generated from the unstamped original; rebuild them.
158 + $metadata = \wp_generate_attachment_metadata($imageId, $file);
159 + \wp_update_attachment_metadata($imageId, $metadata);
72 160 }
73 161 }