PluginProbe
Polylang / trunk
Polylang vtrunk
3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 All 234 releases
polylang / src / modules / site-health / admin-site-health.php

admin-site-health.php in Polylang trunk, at src/modules/site-health/admin-site-health.php

397 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * Class PLL_Admin_Site_Health to add debug info in WP Site Health.
8 *
9 * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ since WordPress 5.2
10 *
11 * @since 2.8
12 */
13 class PLL_Admin_Site_Health {
14 /**
15 * A reference to the PLL_Model instance.
16 *
17 * @since 2.8
18 *
19 * @var PLL_Model
20 */
21 protected $model;
22
23 /**
24 * A reference to the PLL_Admin_Static_Pages instance.
25 *
26 * @since 2.8
27 *
28 * @var PLL_Admin_Static_Pages|null
29 */
30 protected $static_pages;
31
32 /**
33 * PLL_Admin_Site_Health constructor.
34 *
35 * @since 2.8
36 *
37 * @param object $polylang The Polylang object.
38 */
39 public function __construct( &$polylang ) {
40 $this->model = &$polylang->model;
41 $this->static_pages = &$polylang->static_pages;
42
43 // Information tab.
44 add_filter( 'debug_information', array( $this, 'info_options' ), 15 );
45 add_filter( 'debug_information', array( $this, 'info_languages' ), 15 );
46 add_filter( 'debug_information', array( $this, 'info' ), 15 );
47
48 // Tests Tab.
49 add_filter( 'site_status_tests', array( $this, 'status_tests' ) );
50 add_filter( 'site_status_test_php_modules', array( $this, 'site_status_test_php_modules' ) ); // Require simplexml in Site health.
51 }
52
53 /**
54 * Returns a list of keys to exclude from the site health information.
55 *
56 * @since 2.8
57 *
58 * @return string[] List of option keys to ignore.
59 */
60 protected function exclude_options_keys() {
61 return array(
62 'uninstall',
63 'first_activation',
64 );
65 }
66
67 /**
68 * Returns a list of keys to exclude from the site health information.
69 *
70 * @since 2.8
71 *
72 * @return string[] List of language keys to ignore.
73 */
74 protected function exclude_language_keys() {
75 return array(
76 'flag',
77 'host',
78 'taxonomy',
79 'description',
80 'parent',
81 'filter',
82 'custom_flag',
83 );
84 }
85
86 /**
87 * Add Polylang Options to Site Health Information tab.
88 *
89 * @since 2.8
90 * @param array $debug_info The debug information to be added to the core information page.
91 *
92 * @return array
93 */
94 public function info_options( $debug_info ) {
95 $fields = $this->model->options->get_site_health_info();
96
97 // Get effective translated post types and taxonomies. The options doesn't show all translated ones.
98 if ( ! empty( $this->model->get_translated_post_types() ) ) {
99 $fields['cpt']['label'] = __( 'Translated post types', 'polylang' );
100 $fields['cpt']['value'] = implode( ', ', $this->model->get_translated_post_types() );
101 }
102 if ( ! empty( $this->model->get_translated_taxonomies() ) ) {
103 $fields['taxonomies']['label'] = __( 'Translated taxonomies', 'polylang' );
104 $fields['taxonomies']['value'] = implode( ', ', $this->model->get_translated_taxonomies() );
105 }
106
107 $debug_info['pll_options'] = array(
108 /* translators: placeholder is the plugin name */
109 'label' => sprintf( __( '%s options', 'polylang' ), POLYLANG ),
110 'fields' => $fields,
111 );
112
113 return $debug_info;
114 }
115
116 /**
117 * Adds Polylang Languages settings to Site Health Information tab.
118 *
119 * @since 2.8
120 *
121 * @param array $debug_info The debug information to be added to the core information page.
122 * @return array
123 */
124 public function info_languages( $debug_info ) {
125 foreach ( $this->model->get_languages_list() as $language ) {
126 $fields = array();
127
128 foreach ( $language->to_array() as $key => $value ) {
129 if ( in_array( $key, $this->exclude_language_keys(), true ) ) {
130 continue;
131 }
132
133 if ( empty( $value ) ) {
134 $value = '0';
135 }
136
137 $fields[ $key ]['label'] = $key;
138
139 if ( 'term_props' === $key && is_array( $value ) ) {
140 $fields[ $key ]['value'] = $this->get_info_term_props( $value );
141 } else {
142 $fields[ $key ]['value'] = $value;
143 }
144
145 if ( 'term_group' === $key ) {
146 $fields[ $key ]['label'] = 'order'; // Changed for readability but not translated as other keys are not.
147 }
148 }
149
150 $debug_info[ 'pll_language_' . $language->slug ] = array(
151 /* translators: %1$s placeholder is the language name, %2$s is the language code */
152 'label' => sprintf( __( 'Language: %1$s - %2$s', 'polylang' ), $language->name, $language->slug ),
153 /* translators: placeholder is the flag image */
154 'description' => sprintf( esc_html__( 'Flag used in the language switcher: %s', 'polylang' ), $this->get_flag( $language ) ),
155 'fields' => $fields,
156 );
157 }
158
159 return $debug_info;
160 }
161
162 /**
163 * Adds term props data to the info languages array.
164 *
165 * @since 3.4
166 *
167 * @param array $value The term props data.
168 * @return array The term props data formatted for the info languages tab.
169 */
170 protected function get_info_term_props( $value ) {
171 $return_value = array();
172
173 foreach ( $value as $language_taxonomy => $item ) {
174 $language_taxonomy_array = array_fill( 0, count( $item ), $language_taxonomy );
175
176 $keys_with_language_taxonomy = array_map(
177 function ( $key, $language_taxonomy ) {
178 return "{$language_taxonomy}/{$key}";
179 },
180 array_keys( $item ),
181 $language_taxonomy_array
182 );
183
184 $value = array_combine( $keys_with_language_taxonomy, $item );
185 if ( is_array( $value ) ) {
186 $return_value = array_merge( $return_value, $value );
187 }
188 }
189 return $return_value;
190 }
191
192 /**
193 * Returns the flag used in the language switcher.
194 *
195 * @since 2.8
196 *
197 * @param PLL_Language $language Language object.
198 * @return string
199 */
200 protected function get_flag( $language ) {
201 $flag = $language->get_display_flag();
202 return empty( $flag ) ? '<span>' . esc_html__( 'Undefined', 'polylang' ) . '</span>' : $flag;
203 }
204
205 /**
206 * Add a Site Health test on homepage translation.
207 *
208 * @since 2.8
209 *
210 * @param array $tests Array with tests declaration data.
211 * @return array
212 */
213 public function status_tests( $tests ) {
214 // Add the test only if the homepage is an existing static page.
215 if ( 'page' !== get_option( 'show_on_front' ) ) {
216 return $tests;
217 }
218
219 $page_on_front = get_option( 'page_on_front' );
220
221 if ( ! is_numeric( $page_on_front ) || ! get_post( (int) $page_on_front ) ) {
222 return $tests;
223 }
224
225 $tests['direct']['pll_homepage'] = array(
226 'label' => esc_html__( 'Homepage translated', 'polylang' ),
227 'test' => array( $this, 'homepage_test' ),
228 );
229
230 return $tests;
231 }
232
233 /**
234 * Test if the home page is translated or not.
235 *
236 * @since 2.8
237 *
238 * @return array $result Array with test results.
239 */
240 public function homepage_test() {
241 $result = array(
242 'label' => __( 'All languages have a translated homepage', 'polylang' ),
243 'status' => 'good',
244 'badge' => array(
245 'label' => POLYLANG,
246 'color' => 'blue',
247 ),
248 'description' => sprintf(
249 '<p>%s</p>',
250 esc_html__( 'It is mandatory to translate the static front page in all languages.', 'polylang' )
251 ),
252 'actions' => '',
253 'test' => 'pll_homepage',
254 );
255
256 $message = $this->static_pages->get_must_translate_message();
257
258 if ( ! empty( $message ) ) {
259 $result['status'] = 'critical';
260 $result['label'] = __( 'The homepage is not translated in all languages', 'polylang' );
261 $result['description'] = sprintf( '<p>%s</p>', $message );
262 }
263 return $result;
264 }
265
266 /**
267 * Add Polylang Warnings to Site Health Information tab.
268 *
269 * @since 3.1
270 *
271 * @param array $debug_info The debug information to be added to the core information page.
272 * @return array
273 */
274 public function info( $debug_info ) {
275 $fields = array();
276
277 // Add Post Types without languages.
278 $posts_no_lang = $this->get_post_ids_without_lang();
279
280 if ( ! empty( $posts_no_lang ) ) {
281 $fields['post-no-lang']['label'] = __( 'Posts without language', 'polylang' );
282 $fields['post-no-lang']['value'] = $posts_no_lang;
283 }
284
285 $terms_no_lang = $this->get_term_ids_without_lang();
286
287 if ( ! empty( $terms_no_lang ) ) {
288 $fields['term-no-lang']['label'] = __( 'Terms without language', 'polylang' );
289 $fields['term-no-lang']['value'] = $terms_no_lang;
290 }
291
292 // Add WPML files.
293 $wpml_files = PLL_WPML_Config::instance()->get_files();
294 if ( ! empty( $wpml_files ) ) {
295 $fields['wpml']['label'] = 'wpml-config.xml files';
296 $fields['wpml']['value'] = $wpml_files;
297
298 if ( ! extension_loaded( 'simplexml' ) ) {
299 $fields['simplexml']['label'] = __( 'PHP SimpleXML extension', 'polylang' );
300 $fields['simplexml']['value'] = __( 'Not loaded. Contact your host provider.', 'polylang' );
301 }
302 }
303
304 // Multisite
305 if ( is_multisite() ) {
306 if ( is_plugin_active_for_network( POLYLANG_BASENAME ) ) {
307 $network_activated = __( 'Yes', 'polylang' );
308 } else {
309 $network_activated = __( 'No', 'polylang' );
310 }
311 $fields['network_activated'] = array(
312 'label' => __( 'Network activated', 'polylang' ),
313 'value' => $network_activated,
314 );
315 }
316
317 // Create the section.
318 if ( ! empty( $fields ) ) {
319 $debug_info['pll_warnings'] = array(
320 /* translators: placeholder is the plugin name */
321 'label' => sprintf( __( '%s information', 'polylang' ), POLYLANG ),
322 'fields' => $fields,
323 );
324 }
325
326 return $debug_info;
327 }
328
329 /**
330 * Get an array with post_type as key and post ids as value.
331 *
332 * @since 3.1
333 *
334 * @param int $limit Max number of posts to show per post type. `-1` to return all of them. Default is 5.
335 *
336 * @return string[] An associative array where the keys are post types and the values
337 * are comma-separated strings of post IDs without a language.
338 *
339 * @phpstan-param -1|positive-int $limit *
340 */
341 public function get_post_ids_without_lang( $limit = 5 ) {
342 $posts = array();
343
344 foreach ( $this->model->get_translated_post_types() as $post_type ) {
345 $post_ids_with_no_language = $this->model->get_posts_with_no_lang( $post_type, $limit );
346
347 if ( ! empty( $post_ids_with_no_language ) ) {
348 $posts[ $post_type ] = implode( ',', $post_ids_with_no_language );
349 }
350 }
351 return $posts;
352 }
353
354 /**
355 * Get an array with taxonomy as key and term ids as value.
356 *
357 * @since 3.1
358 * @param int $limit Max number of terms to show per post type. `-1` to return all of them. Default is 5.
359 *
360 * @return string[] An associative array where the keys are post types and the values
361 * are comma-separated strings of post IDs without a language.
362 * @phpstan-param -1|positive-int $limit
363 */
364 public function get_term_ids_without_lang( $limit = 5 ) {
365 $terms = array();
366
367 foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) {
368 $term_ids_with_no_language = $this->model->get_terms_with_no_lang( $taxonomy, $limit );
369
370 if ( ! empty( $term_ids_with_no_language ) ) {
371 $terms[ $taxonomy ] = implode( ',', $term_ids_with_no_language );
372 }
373 }
374 return $terms;
375 }
376
377 /**
378 * Requires the simplexml PHP module when a wpml-config.xml has been found.
379 *
380 * @since 3.1
381 * @since 3.2 Moved from PLL_WPML_Config
382 *
383 * @param array $modules An associative array of modules to test for.
384 * @return array
385 */
386 public function site_status_test_php_modules( $modules ) {
387 $files = PLL_WPML_Config::instance()->get_files();
388 if ( ! empty( $files ) ) {
389 $modules['simplexml'] = array(
390 'extension' => 'simplexml',
391 'required' => true,
392 );
393 }
394 return $modules;
395 }
396 }
397