Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
65.62% |
84 / 128 |
|
14.29% |
1 / 7 |
CRAP | |
0.00% |
0 / 1 |
SelectVoice | |
65.62% |
84 / 128 |
|
14.29% |
1 / 7 |
59.85 | |
0.00% |
0 / 1 |
init | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
element | |
96.67% |
58 / 60 |
|
0.00% |
0 / 1 |
9 | |||
save | |
75.00% |
18 / 24 |
|
0.00% |
0 / 1 |
10.27 | |||
restApiInit | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
languagesRestApiResponse | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
voicesRestApiResponse | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
adminEnqueueScripts | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | /** |
6 | * BeyondWords Component: Select Voice |
7 | * |
8 | * @package Beyondwords\Wordpress |
9 | * @author Stuart McAlpine <stu@beyondwords.io> |
10 | * @since 4.0.0 |
11 | */ |
12 | |
13 | namespace Beyondwords\Wordpress\Component\Post\SelectVoice; |
14 | |
15 | use Beyondwords\Wordpress\Core\CoreUtils; |
16 | use Beyondwords\Wordpress\Component\Settings\SettingsUtils; |
17 | use Beyondwords\Wordpress\Core\ApiClient; |
18 | |
19 | /** |
20 | * SelectVoice |
21 | * |
22 | * @since 4.0.0 |
23 | */ |
24 | class SelectVoice |
25 | { |
26 | /** |
27 | * Init. |
28 | * |
29 | * @since 4.0.0 |
30 | */ |
31 | public function init() |
32 | { |
33 | add_action('rest_api_init', array($this, 'restApiInit')); |
34 | add_action('admin_enqueue_scripts', array($this, 'adminEnqueueScripts')); |
35 | |
36 | add_action('wp_loaded', function () { |
37 | $postTypes = SettingsUtils::getCompatiblePostTypes(); |
38 | |
39 | if (is_array($postTypes)) { |
40 | foreach ($postTypes as $postType) { |
41 | add_action("save_post_{$postType}", array($this, 'save'), 10); |
42 | } |
43 | } |
44 | }); |
45 | } |
46 | |
47 | /** |
48 | * HTML output for this component. |
49 | * |
50 | * @since 4.0.0 |
51 | * @since 4.5.1 Hide element if no language data exists. |
52 | * @since 5.4.0 Always display all languages and associated voices. |
53 | * |
54 | * @param WP_Post $post The post object. |
55 | * |
56 | * @return string|null |
57 | */ |
58 | public function element($post) |
59 | { |
60 | $postLanguageCode = get_post_meta($post->ID, 'beyondwords_language_code', true); |
61 | $postVoiceId = get_post_meta($post->ID, 'beyondwords_body_voice_id', true); |
62 | |
63 | $languageCode = $postLanguageCode ?: get_option('beyondwords_project_language_code'); |
64 | $voiceId = $postVoiceId ?: get_option('beyondwords_project_body_voice_id'); |
65 | |
66 | $languages = ApiClient::getLanguages(); |
67 | $voices = ApiClient::getVoices($languageCode); |
68 | |
69 | if (! is_array($voices)) { |
70 | $voices = []; |
71 | } |
72 | |
73 | wp_nonce_field('beyondwords_select_voice', 'beyondwords_select_voice_nonce'); |
74 | ?> |
75 | <p |
76 | id="beyondwords-metabox-select-voice--language-code" |
77 | class="post-attributes-label-wrapper page-template-label-wrapper" |
78 | > |
79 | <label class="post-attributes-label" for="beyondwords_language_code"> |
80 | Language |
81 | </label> |
82 | </p> |
83 | <select id="beyondwords_language_code" name="beyondwords_language_code" style="width: 100%;"> |
84 | <?php |
85 | foreach ($languages as $language) { |
86 | if (empty($language['code']) || empty($language['name']) || empty($language['accent'])) { |
87 | continue; |
88 | } |
89 | printf( |
90 | '<option value="%s" data-default-voice-id="%s" %s>%s (%s)</option>', |
91 | esc_attr($language['code']), |
92 | esc_attr($language['default_voices']['body']['id'] ?? ''), |
93 | selected(strval($language['code']), strval($languageCode)), |
94 | esc_html($language['name']), |
95 | esc_html($language['accent']) |
96 | ); |
97 | } |
98 | ?> |
99 | </select> |
100 | <p |
101 | id="beyondwords-metabox-select-voice--voice-id" |
102 | class="post-attributes-label-wrapper page-template-label-wrapper" |
103 | > |
104 | <label class="post-attributes-label" for="beyondwords_voice_id"> |
105 | Voice |
106 | </label> |
107 | </p> |
108 | <select |
109 | id="beyondwords_voice_id" |
110 | name="beyondwords_voice_id" |
111 | style="width: 100%;" |
112 | <?php echo disabled(!strval($languageCode)) ?> |
113 | > |
114 | <?php |
115 | foreach ($voices as $voice) { |
116 | printf( |
117 | '<option value="%s" %s>%s</option>', |
118 | esc_attr($voice['id']), |
119 | selected(strval($voice['id']), strval($voiceId)), |
120 | esc_html($voice['name']) |
121 | ); |
122 | } |
123 | ?> |
124 | </select> |
125 | <img |
126 | src="/wp-admin/images/spinner.gif" |
127 | class="beyondwords-settings__loader" |
128 | style="display:none; padding: 3px 0;" |
129 | /> |
130 | <?php |
131 | } |
132 | |
133 | /** |
134 | * Save the meta when the post is saved. |
135 | * |
136 | * @since 4.0.0 |
137 | * |
138 | * @param int $postId The ID of the post being saved. |
139 | */ |
140 | public function save($postId) |
141 | { |
142 | if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { |
143 | return $postId; |
144 | } |
145 | |
146 | // "save_post" can be triggered at other times, so verify this request came from the our component |
147 | if ( |
148 | ! isset($_POST['beyondwords_language_code']) || |
149 | ! isset($_POST['beyondwords_voice_id']) || |
150 | ! isset($_POST['beyondwords_select_voice_nonce']) |
151 | ) { |
152 | return $postId; |
153 | } |
154 | |
155 | // "save_post" can be triggered at other times, so verify this request came from the our component |
156 | if ( |
157 | ! wp_verify_nonce( |
158 | sanitize_key($_POST['beyondwords_select_voice_nonce']), |
159 | 'beyondwords_select_voice' |
160 | ) |
161 | ) { |
162 | return $postId; |
163 | } |
164 | |
165 | $languageCode = sanitize_text_field(wp_unslash($_POST['beyondwords_language_code'])); |
166 | |
167 | if (! empty($languageCode)) { |
168 | update_post_meta($postId, 'beyondwords_language_code', $languageCode); |
169 | } else { |
170 | delete_post_meta($postId, 'beyondwords_language_code'); |
171 | } |
172 | |
173 | $voiceId = sanitize_text_field(wp_unslash($_POST['beyondwords_voice_id'])); |
174 | |
175 | if (! empty($voiceId)) { |
176 | update_post_meta($postId, 'beyondwords_body_voice_id', $voiceId); |
177 | update_post_meta($postId, 'beyondwords_title_voice_id', $voiceId); |
178 | update_post_meta($postId, 'beyondwords_summary_voice_id', $voiceId); |
179 | } else { |
180 | delete_post_meta($postId, 'beyondwords_body_voice_id'); |
181 | delete_post_meta($postId, 'beyondwords_title_voice_id'); |
182 | delete_post_meta($postId, 'beyondwords_summary_voice_id'); |
183 | } |
184 | |
185 | return $postId; |
186 | } |
187 | |
188 | /** |
189 | * Register WP REST API route |
190 | * |
191 | * @since 4.0.0 |
192 | * |
193 | * @return void |
194 | */ |
195 | public function restApiInit() |
196 | { |
197 | // Languages endpoint |
198 | register_rest_route('beyondwords/v1', '/languages', array( |
199 | 'methods' => \WP_REST_Server::READABLE, |
200 | 'callback' => array($this, 'languagesRestApiResponse'), |
201 | 'permission_callback' => function () { |
202 | return current_user_can('edit_posts'); |
203 | }, |
204 | )); |
205 | |
206 | // Voices endpoint |
207 | register_rest_route('beyondwords/v1', '/languages/(?P<languageCode>[a-zA-Z0-9-_]+)/voices', array( |
208 | 'methods' => \WP_REST_Server::READABLE, |
209 | 'callback' => array($this, 'voicesRestApiResponse'), |
210 | 'permission_callback' => function () { |
211 | return current_user_can('edit_posts'); |
212 | }, |
213 | )); |
214 | } |
215 | |
216 | /** |
217 | * "Languages" WP REST API response (required for the Gutenberg editor). |
218 | * |
219 | * @since 4.0.0 |
220 | * @since 5.4.0 No longer filter by "Languages" plugin setting. |
221 | * |
222 | * @return \WP_REST_Response |
223 | */ |
224 | public function languagesRestApiResponse() |
225 | { |
226 | $languages = ApiClient::getLanguages(); |
227 | |
228 | return new \WP_REST_Response($languages); |
229 | } |
230 | |
231 | /** |
232 | * "Voices" WP REST API response (required for the Gutenberg editor |
233 | * and Block Editor). |
234 | * |
235 | * @since 4.0.0 |
236 | * |
237 | * @return \WP_REST_Response |
238 | */ |
239 | public function voicesRestApiResponse(\WP_REST_Request $data) |
240 | { |
241 | $params = $data->get_url_params(); |
242 | |
243 | $voices = ApiClient::getVoices($params['languageCode']); |
244 | |
245 | return new \WP_REST_Response($voices); |
246 | } |
247 | |
248 | /** |
249 | * Register the component scripts. |
250 | * |
251 | * @since 4.0.0 |
252 | * |
253 | * @param string $hook Page hook |
254 | * |
255 | * @return void |
256 | */ |
257 | public function adminEnqueueScripts($hook) |
258 | { |
259 | if (! CoreUtils::isGutenbergPage() && ( $hook === 'post.php' || $hook === 'post-new.php')) { |
260 | wp_register_script( |
261 | 'beyondwords-metabox--select-voice', |
262 | BEYONDWORDS__PLUGIN_URI . 'src/Component/Post/SelectVoice/classic-metabox.js', |
263 | ['jquery', 'underscore'], |
264 | BEYONDWORDS__PLUGIN_VERSION, |
265 | true |
266 | ); |
267 | |
268 | /** |
269 | * Localize the script to handle ajax requests |
270 | */ |
271 | wp_localize_script( |
272 | 'beyondwords-metabox--select-voice', |
273 | 'beyondwordsData', |
274 | array( |
275 | 'nonce' => wp_create_nonce('wp_rest'), |
276 | 'root' => esc_url_raw(rest_url()), |
277 | ) |
278 | ); |
279 | |
280 | wp_enqueue_script('beyondwords-metabox--select-voice'); |
281 | } |
282 | } |
283 | } |