Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
79.37% |
150 / 189 |
|
66.67% |
14 / 21 |
CRAP | |
0.00% |
0 / 1 |
ApiClient | |
79.37% |
150 / 189 |
|
66.67% |
14 / 21 |
91.63 | |
0.00% |
0 / 1 |
getContent | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
createAudio | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
updateAudio | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
3.01 | |||
deleteAudio | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
batchDeleteAudio | |
84.85% |
28 / 33 |
|
0.00% |
0 / 1 |
8.22 | |||
getLanguages | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getVoices | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
getVoice | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
updateVoice | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getProject | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
updateProject | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
getPlayerSettings | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
updatePlayerSettings | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
getVideoSettings | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
callApi | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
5 | |||
buildRequestArgs | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
errorMessageFromResponse | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
5 | |||
deleteErrors | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
addRequestLogs | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
deleteLogs | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
saveErrorMessage | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Beyondwords\Wordpress\Core; |
6 | |
7 | use Beyondwords\Wordpress\Core\Environment; |
8 | use Beyondwords\Wordpress\Core\Request; |
9 | use Beyondwords\Wordpress\Component\Post\PostContentUtils; |
10 | use Beyondwords\Wordpress\Component\Post\PostMetaUtils; |
11 | |
12 | /** |
13 | * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
14 | **/ |
15 | class ApiClient |
16 | { |
17 | /** |
18 | * Error format. |
19 | * |
20 | * The error format used to display error messages in WordPress admin. |
21 | * |
22 | * @var string |
23 | */ |
24 | public const ERROR_FORMAT = '#%s: %s'; |
25 | |
26 | /** |
27 | * GET /projects/:id/content/:id. |
28 | * |
29 | * @param string $contentId BeyomndWords Content ID |
30 | * @param int|string $projectId BeyondWords Project ID, optional. |
31 | * |
32 | * @return WP_Response|false |
33 | **/ |
34 | public static function getContent($contentId, $projectId = null) |
35 | { |
36 | if (! $projectId) { |
37 | $projectId = PostMetaUtils::getProjectId($contentId); |
38 | } |
39 | |
40 | if (! $projectId || ! $contentId) { |
41 | return false; |
42 | } |
43 | |
44 | $url = sprintf('%s/projects/%d/content/%s', Environment::getApiUrl(), $projectId, $contentId); |
45 | |
46 | $request = new Request('GET', $url); |
47 | |
48 | return self::callApi($request); |
49 | } |
50 | |
51 | /** |
52 | * POST /projects/:id/content. |
53 | * |
54 | * @since 3.0.0 |
55 | * @since 5.2.0 Make static. |
56 | * |
57 | * @param int $postId WordPress Post ID |
58 | * |
59 | * @return mixed JSON-decoded response body |
60 | **/ |
61 | public static function createAudio($postId) |
62 | { |
63 | $projectId = PostMetaUtils::getProjectId($postId); |
64 | |
65 | if (! $projectId) { |
66 | return false; |
67 | } |
68 | |
69 | $url = sprintf('%s/projects/%d/content', Environment::getApiUrl(), $projectId); |
70 | |
71 | $body = PostContentUtils::getContentParams($postId); |
72 | |
73 | $request = new Request('POST', $url, $body); |
74 | $response = self::callApi($request, $postId); |
75 | |
76 | return json_decode(wp_remote_retrieve_body($response), true); |
77 | } |
78 | |
79 | /** |
80 | * PUT /projects/:id/content/:id. |
81 | * |
82 | * @since 3.0.0 |
83 | * @since 5.2.0 Make static. |
84 | * |
85 | * @param int $postId WordPress Post ID |
86 | * |
87 | * @return mixed JSON-decoded response body |
88 | **/ |
89 | public static function updateAudio($postId) |
90 | { |
91 | $projectId = PostMetaUtils::getProjectId($postId); |
92 | $contentId = PostMetaUtils::getContentId($postId); |
93 | |
94 | if (! $projectId || ! $contentId) { |
95 | return false; |
96 | } |
97 | |
98 | $url = sprintf('%s/projects/%d/content/%s', Environment::getApiUrl(), $projectId, $contentId); |
99 | |
100 | $body = PostContentUtils::getContentParams($postId); |
101 | |
102 | $request = new Request('PUT', $url, $body); |
103 | $response = self::callApi($request, $postId); |
104 | |
105 | return json_decode(wp_remote_retrieve_body($response), true); |
106 | } |
107 | |
108 | /** |
109 | * DELETE /projects/:id/content/:id. |
110 | * |
111 | * @since 3.0.0 |
112 | * @since 5.2.0 Make static. |
113 | * |
114 | * @param int $postId WordPress Post ID |
115 | * |
116 | * @return mixed JSON-decoded response body |
117 | **/ |
118 | public static function deleteAudio($postId) |
119 | { |
120 | $projectId = PostMetaUtils::getProjectId($postId); |
121 | $contentId = PostMetaUtils::getContentId($postId); |
122 | |
123 | if (! $projectId || ! $contentId) { |
124 | return false; |
125 | } |
126 | |
127 | $url = sprintf('%s/projects/%d/content/%s', Environment::getApiUrl(), $projectId, $contentId); |
128 | |
129 | $request = new Request('DELETE', $url); |
130 | $response = self::callApi($request, $postId); |
131 | $code = wp_remote_retrieve_response_code($response); |
132 | |
133 | // Expect 204 Deleted |
134 | if ($code !== 204) { |
135 | return false; |
136 | } |
137 | |
138 | return json_decode(wp_remote_retrieve_body($response), true); |
139 | } |
140 | |
141 | /** |
142 | * DELETE /projects/:id/content/:id. |
143 | * |
144 | * @since 4.1.0 |
145 | * @since 5.2.0 Make static. |
146 | * @since 5.2.2 Remove sslverify param & increase timeout to 30s for REST API calls. |
147 | * |
148 | * @param int[] $postIds Array of WordPress Post IDs. |
149 | * |
150 | * @throws \Exception |
151 | * @return mixed JSON-decoded response body |
152 | **/ |
153 | public static function batchDeleteAudio($postIds) |
154 | { |
155 | $contentIds = []; |
156 | $updatedPostIds = []; |
157 | |
158 | foreach ($postIds as $postId) { |
159 | $projectId = PostMetaUtils::getProjectId($postId); |
160 | |
161 | if (! $projectId) { |
162 | continue; |
163 | } |
164 | |
165 | $contentId = PostMetaUtils::getContentId($postId); |
166 | |
167 | if (! $contentId) { |
168 | continue; |
169 | } |
170 | |
171 | $contentIds[$projectId][] = $contentId; |
172 | $updatedPostIds[] = $postId; |
173 | } |
174 | |
175 | if (! count($contentIds)) { |
176 | throw new \Exception(esc_html__('None of the selected posts had valid BeyondWords audio data.', 'speechkit')); // phpcs:ignore Generic.Files.LineLength.TooLong |
177 | } |
178 | |
179 | if (count($contentIds) > 1) { |
180 | throw new \Exception(esc_html__('Batch delete can only be performed on audio belonging a single project.', 'speechkit')); // phpcs:ignore Generic.Files.LineLength.TooLong |
181 | } |
182 | |
183 | $projectId = array_key_first($contentIds); |
184 | |
185 | $url = sprintf('%s/projects/%d/content/batch_delete', Environment::getApiUrl(), $projectId); |
186 | |
187 | $body = wp_json_encode(['ids' => $contentIds[$projectId]]); |
188 | |
189 | $request = new Request('POST', $url, $body); |
190 | |
191 | $args = array( |
192 | 'blocking' => true, |
193 | 'body' => $request->getBody(), |
194 | 'headers' => $request->getHeaders(), |
195 | 'method' => $request->getMethod(), |
196 | 'timeout' => 30, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout |
197 | ); |
198 | |
199 | $response = wp_remote_request($request->getUrl(), $args); |
200 | |
201 | // WordPress error performing API call |
202 | if (is_wp_error($response)) { |
203 | throw new \Exception(esc_html($response->get_error_message())); |
204 | } |
205 | |
206 | $responseCode = wp_remote_retrieve_response_code($response); |
207 | |
208 | if ($responseCode <= 299) { |
209 | // An OK response means all content IDs in the request were deleted |
210 | return $updatedPostIds; |
211 | } else { |
212 | // For non-OK responses we do not want to delete any custom fields, |
213 | // so return an empty array |
214 | return []; |
215 | } |
216 | } |
217 | |
218 | /** |
219 | * GET /organization/languages |
220 | * |
221 | * @since 4.0.0 Introduced |
222 | * @since 4.0.2 Prefix endpoint with /organization |
223 | * @since 5.0.0 Cache response using transients |
224 | * @since 5.2.0 Make static. |
225 | * |
226 | * @return mixed JSON-decoded response body |
227 | **/ |
228 | public static function getLanguages() |
229 | { |
230 | $url = sprintf('%s/organization/languages', Environment::getApiUrl()); |
231 | |
232 | $request = new Request('GET', $url); |
233 | $response = self::callApi($request); |
234 | |
235 | return json_decode(wp_remote_retrieve_body($response), true); |
236 | } |
237 | |
238 | /** |
239 | * GET /organization/voices |
240 | * |
241 | * @since 4.0.0 Introduced |
242 | * @since 4.0.2 Prefix endpoint with /organization |
243 | * @since 4.5.1 Check the $languageId param is numeric. |
244 | * @since 5.0.0 Accept numeric language ID or string language code as param. |
245 | * @since 5.2.0 Make static. |
246 | * |
247 | * @param int|string $language BeyondWords Language code or numeric ID |
248 | * |
249 | * @return mixed JSON-decoded response body |
250 | **/ |
251 | public static function getVoices($languageCode) |
252 | { |
253 | $url = sprintf( |
254 | '%s/organization/voices?filter[language.code]=%s&filter[scopes][]=primary&filter[scopes][]=secondary', |
255 | Environment::getApiUrl(), |
256 | urlencode(strval($languageCode)) |
257 | ); |
258 | |
259 | $request = new Request('GET', $url); |
260 | $response = self::callApi($request); |
261 | |
262 | return json_decode(wp_remote_retrieve_body($response), true); |
263 | } |
264 | |
265 | /** |
266 | * Loops though GET /organization/voices, because |
267 | * GET /organization/voice is not available. |
268 | * |
269 | * @since 5.4.0 |
270 | * |
271 | * @param int $voiceId Voice ID. |
272 | * @param int|false $languageCode Language code, optional. |
273 | * |
274 | * @return object|false Voice, or false if not found. |
275 | **/ |
276 | public static function getVoice($voiceId, $languageCode = false) |
277 | { |
278 | if (! $languageCode) { |
279 | $languageCode = get_option('beyondwords_project_language_code'); |
280 | } |
281 | |
282 | $voices = self::getVoices($languageCode); |
283 | |
284 | if (empty($voices)) { |
285 | return false; |
286 | } |
287 | |
288 | return array_column($voices, null, 'id')[$voiceId] ?? false; |
289 | } |
290 | |
291 | /** |
292 | * PUT /voices/:id. |
293 | * |
294 | * @since 5.0.0 |
295 | * @since 5.2.0 Make static. |
296 | * |
297 | * @param array $settings Associative array of voice settings. |
298 | * |
299 | * @return mixed JSON-decoded response body |
300 | **/ |
301 | public static function updateVoice($voiceId, $settings) |
302 | { |
303 | if (empty($voiceId)) { |
304 | return false; |
305 | } |
306 | |
307 | $url = sprintf('%s/organization/voices/%d', Environment::getApiUrl(), $voiceId); |
308 | |
309 | $request = new Request('PUT', $url, wp_json_encode($settings)); |
310 | $response = self::callApi($request); |
311 | |
312 | return json_decode(wp_remote_retrieve_body($response), true); |
313 | } |
314 | |
315 | /** |
316 | * GET /projects/:id. |
317 | * |
318 | * @since 4.0.0 |
319 | * @since 5.0.0 Cache response using transients |
320 | * @since 5.2.0 Make static. |
321 | * |
322 | * @return mixed JSON-decoded response body |
323 | **/ |
324 | public static function getProject() |
325 | { |
326 | $projectId = get_option('beyondwords_project_id'); |
327 | |
328 | if (! $projectId) { |
329 | return false; |
330 | } |
331 | |
332 | $url = sprintf('%s/projects/%d', Environment::getApiUrl(), $projectId); |
333 | |
334 | $request = new Request('GET', $url); |
335 | $response = self::callApi($request); |
336 | |
337 | return json_decode(wp_remote_retrieve_body($response), true); |
338 | } |
339 | |
340 | /** |
341 | * PUT /projects/:id. |
342 | * |
343 | * @since 5.0.0 |
344 | * @since 5.2.0 Make static. |
345 | * |
346 | * @param array $settings Associative array of project settings. |
347 | * |
348 | * @return mixed JSON-decoded response body |
349 | **/ |
350 | public static function updateProject($settings) |
351 | { |
352 | $projectId = get_option('beyondwords_project_id'); |
353 | |
354 | if (! $projectId) { |
355 | return false; |
356 | } |
357 | |
358 | $url = sprintf('%s/projects/%d', Environment::getApiUrl(), $projectId); |
359 | |
360 | $request = new Request('PUT', $url, wp_json_encode($settings)); |
361 | $response = self::callApi($request); |
362 | |
363 | return json_decode(wp_remote_retrieve_body($response), true); |
364 | } |
365 | |
366 | /** |
367 | * GET /projects/:id/player_settings. |
368 | * |
369 | * @since 4.0.0 |
370 | * @since 5.2.0 Make static. |
371 | * |
372 | * @return mixed JSON-decoded response body |
373 | **/ |
374 | public static function getPlayerSettings() |
375 | { |
376 | $projectId = get_option('beyondwords_project_id'); |
377 | |
378 | if (! $projectId) { |
379 | return false; |
380 | } |
381 | |
382 | $url = sprintf('%s/projects/%d/player_settings', Environment::getApiUrl(), $projectId); |
383 | |
384 | $request = new Request('GET', $url); |
385 | $response = self::callApi($request); |
386 | |
387 | return json_decode(wp_remote_retrieve_body($response), true); |
388 | } |
389 | |
390 | /** |
391 | * PUT /projects/:id/player_settings. |
392 | * |
393 | * @since 4.0.0 |
394 | * @since 5.2.0 Make static. |
395 | * |
396 | * @param array $settings Associative array of player settings. |
397 | * |
398 | * @return mixed JSON-decoded response body |
399 | **/ |
400 | public static function updatePlayerSettings($settings) |
401 | { |
402 | $projectId = get_option('beyondwords_project_id'); |
403 | |
404 | if (! $projectId) { |
405 | return false; |
406 | } |
407 | |
408 | $url = sprintf('%s/projects/%d/player_settings', Environment::getApiUrl(), $projectId); |
409 | |
410 | $request = new Request('PUT', $url, wp_json_encode($settings)); |
411 | $response = self::callApi($request); |
412 | |
413 | return json_decode(wp_remote_retrieve_body($response), true); |
414 | } |
415 | |
416 | /** |
417 | * GET /projects/:id/video_settings. |
418 | * |
419 | * @since 4.1.0 |
420 | * @since 5.0.0 Cache response using transients |
421 | * @since 5.2.0 Make static. |
422 | * |
423 | * @param int $projectId BeyondWords Project ID. |
424 | * |
425 | * @return mixed JSON-decoded response body |
426 | **/ |
427 | public static function getVideoSettings($projectId = null) |
428 | { |
429 | if (! $projectId) { |
430 | $projectId = get_option('beyondwords_project_id'); |
431 | |
432 | if (! $projectId) { |
433 | return false; |
434 | } |
435 | } |
436 | |
437 | $url = sprintf('%s/projects/%d/video_settings', Environment::getApiUrl(), (int)$projectId); |
438 | |
439 | $request = new Request('GET', $url); |
440 | $response = self::callApi($request); |
441 | |
442 | return json_decode(wp_remote_retrieve_body($response), true); |
443 | } |
444 | |
445 | /** |
446 | * Call the BeyondWords API backend, logging any errors if the requests for a particular post. |
447 | * |
448 | * @todo investigate whether we can move the logging into a WordPress HTTP filter. |
449 | * |
450 | * @since 3.0.0 |
451 | * @since 3.9.0 Stop saving the speechkit_status post meta - downgrades to plugin v2.x are no longer expected. |
452 | * @since 4.0.0 Removed hash comparison. |
453 | * @since 4.4.0 Handle 204 responses with no body. |
454 | * @since 5.2.0 Make static, return result from wp_remote_request. |
455 | * |
456 | * @param Request $request Request. |
457 | * @param int $postId WordPress Post ID |
458 | * |
459 | * @return array|WP_Error The response array or a WP_Error on failure. See WP_Http::request() for |
460 | * information on return value. |
461 | **/ |
462 | public static function callApi($request, $postId = false) |
463 | { |
464 | // By default we delete the request logs we temporarily store |
465 | $deleteRequestLogs = true; |
466 | |
467 | // Delete existing errors before making this API call |
468 | self::deleteErrors($postId); |
469 | |
470 | $args = self::buildRequestArgs($request); |
471 | |
472 | // Log the request details |
473 | self::addRequestLogs($postId, $request, $args); |
474 | |
475 | // Get response |
476 | $response = wp_remote_request($request->getUrl(), $args); |
477 | $responseCode = wp_remote_retrieve_response_code($response); |
478 | |
479 | // Mark API connection as invalid for 401 (API key may have been revoked) |
480 | if ($responseCode === 401) { |
481 | delete_option('beyondwords_valid_api_connection'); |
482 | } |
483 | |
484 | // Save error messages from WordPress HTTP errors and BeyondWords REST API error responses |
485 | if (is_wp_error($response) || $responseCode > 299) { |
486 | $deleteRequestLogs = false; |
487 | |
488 | $message = self::errorMessageFromResponse($response); |
489 | |
490 | self::saveErrorMessage($postId, $message, $responseCode); |
491 | } |
492 | |
493 | if ($deleteRequestLogs) { |
494 | self::deleteLogs($postId); |
495 | } |
496 | |
497 | return $response; |
498 | } |
499 | |
500 | /** |
501 | * Build the request args for wp_remote_request(). |
502 | * |
503 | * @since 3.0.0 |
504 | * @since 4.0.0 Removed hash comparison and display 403 errors. |
505 | * @since 4.1.0 Introduced. |
506 | * @since 5.2.0 Make static. |
507 | * @since 5.2.2 Remove sslverify param & increase timeout to 30s for REST API calls. |
508 | * |
509 | * @param Request $request BeyondWords Request. |
510 | * |
511 | * @return array WordPress HTTP Request arguments. |
512 | */ |
513 | public static function buildRequestArgs($request) |
514 | { |
515 | return [ |
516 | 'blocking' => true, |
517 | 'body' => $request->getBody(), |
518 | 'headers' => $request->getHeaders(), |
519 | 'method' => $request->getMethod(), |
520 | 'timeout' => 30, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout |
521 | ]; |
522 | } |
523 | |
524 | /** |
525 | * Error message from BeyondWords REST API response. |
526 | * |
527 | * @since 4.1.0 |
528 | * @since 5.2.0 Make static. |
529 | * |
530 | * @param mixed[] $response BeyondWords REST API response. |
531 | * |
532 | * @return string Error message. |
533 | */ |
534 | public static function errorMessageFromResponse($response) |
535 | { |
536 | $body = wp_remote_retrieve_body($response); |
537 | $body = json_decode($body, true); |
538 | |
539 | $message = wp_remote_retrieve_response_message($response); |
540 | |
541 | if (is_array($body)) { |
542 | if (array_key_exists('errors', $body)) { |
543 | $messages = []; |
544 | |
545 | foreach ($body['errors'] as $error) { |
546 | $messages[] = implode(' ', array_values($error)); |
547 | } |
548 | |
549 | $message = implode(', ', $messages); |
550 | } elseif (array_key_exists('message', $body)) { |
551 | $message = $body['message']; |
552 | } |
553 | } |
554 | |
555 | return $message; |
556 | } |
557 | |
558 | /** |
559 | * Deletes errors for a post. |
560 | * |
561 | * @since 4.1.0 Introduced. |
562 | * @since 5.2.0 Make static. |
563 | * |
564 | * @param int $postId WordPress post ID. |
565 | * |
566 | * @return void |
567 | */ |
568 | public static function deleteErrors($postId) |
569 | { |
570 | if (! $postId) { |
571 | return; |
572 | } |
573 | |
574 | // Reset any existing errors before making this API call |
575 | delete_post_meta($postId, 'speechkit_error_message'); |
576 | delete_post_meta($postId, 'beyondwords_error_message'); |
577 | } |
578 | |
579 | /** |
580 | * Log the request details for a post. |
581 | * |
582 | * Logs are removed later if the request was successful and retained if not. |
583 | * |
584 | * @since 4.1.0 Introduced. |
585 | * @since 5.2.0 Make static, use wp_json_encode(), don't save headers. |
586 | * |
587 | * @param int $postId WordPress post ID. |
588 | * @param Request $request BeyondWords Request. |
589 | * @param array $args BeyondWords Request args. |
590 | * |
591 | * @return void |
592 | */ |
593 | public static function addRequestLogs($postId, $request, $args) |
594 | { |
595 | if (! $postId) { |
596 | return; |
597 | } |
598 | |
599 | // Don't store headers in the logs |
600 | unset($args['headers']); |
601 | |
602 | update_post_meta($postId, 'beyondwords_request_url', $request->getUrl()); |
603 | update_post_meta($postId, 'beyondwords_request_args', wp_json_encode($args)); |
604 | } |
605 | |
606 | /** |
607 | * Deletes request/response logs for a post. |
608 | * |
609 | * @since 4.1.0 Introduced. |
610 | * @since 5.2.0 Make static. |
611 | * |
612 | * @param int $postId WordPress post ID. |
613 | * |
614 | * @return void |
615 | */ |
616 | public static function deleteLogs($postId) |
617 | { |
618 | if (! $postId) { |
619 | return; |
620 | } |
621 | |
622 | delete_post_meta($postId, 'beyondwords_request_url'); |
623 | delete_post_meta($postId, 'beyondwords_request_args'); |
624 | } |
625 | |
626 | /** |
627 | * Add an error message for a post. |
628 | * |
629 | * @since 4.1.0 Introduced. |
630 | * @since 4.4.0 Rename from error() to saveErrorMessage(). |
631 | * @since 5.2.0 Make static. |
632 | * |
633 | * @param int $postId WordPress post ID. |
634 | * @param string $message Error message. |
635 | * @param int $code Error code. |
636 | * |
637 | * @return void |
638 | */ |
639 | public static function saveErrorMessage($postId, $message = '', $code = 500) |
640 | { |
641 | if (! $postId) { |
642 | return; |
643 | } |
644 | |
645 | if (! $message) { |
646 | $message = sprintf( |
647 | /* translators: %s is replaced with the support email link */ |
648 | esc_html__('API request error. Please contact %s.', 'speechkit'), |
649 | '<a href="mailto:support@beyondwords.io">support@beyondwords.io</a>' |
650 | ); |
651 | } |
652 | |
653 | if (! $code) { |
654 | $code = 500; |
655 | } |
656 | |
657 | update_post_meta( |
658 | $postId, |
659 | 'beyondwords_error_message', |
660 | sprintf(self::ERROR_FORMAT, (string)$code, $message, $code) |
661 | ); |
662 | } |
663 | } |