208 $this->stylesheet = basename( $this->theme_root ) . '/' . $this->stylesheet; |
205 $this->stylesheet = basename( $this->theme_root ) . '/' . $this->stylesheet; |
209 $this->theme_root = dirname( $theme_root ); |
206 $this->theme_root = dirname( $theme_root ); |
210 } |
207 } |
211 |
208 |
212 $this->cache_hash = md5( $this->theme_root . '/' . $this->stylesheet ); |
209 $this->cache_hash = md5( $this->theme_root . '/' . $this->stylesheet ); |
213 $theme_file = $this->stylesheet . '/style.css'; |
210 $theme_file = $this->stylesheet . '/style.css'; |
214 |
211 |
215 $cache = $this->cache_get( 'theme' ); |
212 $cache = $this->cache_get( 'theme' ); |
216 |
213 |
217 if ( is_array( $cache ) ) { |
214 if ( is_array( $cache ) ) { |
218 foreach ( array( 'errors', 'headers', 'template' ) as $key ) { |
215 foreach ( array( 'errors', 'headers', 'template' ) as $key ) { |
219 if ( isset( $cache[ $key ] ) ) |
216 if ( isset( $cache[ $key ] ) ) { |
220 $this->$key = $cache[ $key ]; |
217 $this->$key = $cache[ $key ]; |
221 } |
218 } |
222 if ( $this->errors ) |
219 } |
|
220 if ( $this->errors ) { |
223 return; |
221 return; |
224 if ( isset( $cache['theme_root_template'] ) ) |
222 } |
|
223 if ( isset( $cache['theme_root_template'] ) ) { |
225 $theme_root_template = $cache['theme_root_template']; |
224 $theme_root_template = $cache['theme_root_template']; |
|
225 } |
226 } elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) { |
226 } elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) { |
227 $this->headers['Name'] = $this->stylesheet; |
227 $this->headers['Name'] = $this->stylesheet; |
228 if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) ) |
228 if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) ) { |
229 $this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), esc_html( $this->stylesheet ) ) ); |
229 $this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), esc_html( $this->stylesheet ) ) ); |
230 else |
230 } else { |
231 $this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) ); |
231 $this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) ); |
|
232 } |
232 $this->template = $this->stylesheet; |
233 $this->template = $this->stylesheet; |
233 $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); |
234 $this->cache_add( |
234 if ( ! file_exists( $this->theme_root ) ) // Don't cache this one. |
235 'theme', |
|
236 array( |
|
237 'headers' => $this->headers, |
|
238 'errors' => $this->errors, |
|
239 'stylesheet' => $this->stylesheet, |
|
240 'template' => $this->template, |
|
241 ) |
|
242 ); |
|
243 if ( ! file_exists( $this->theme_root ) ) { // Don't cache this one. |
235 $this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn’t exist. Please check your installation.' ) ); |
244 $this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn’t exist. Please check your installation.' ) ); |
|
245 } |
236 return; |
246 return; |
237 } elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) { |
247 } elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) { |
238 $this->headers['Name'] = $this->stylesheet; |
248 $this->headers['Name'] = $this->stylesheet; |
239 $this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) ); |
249 $this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) ); |
240 $this->template = $this->stylesheet; |
250 $this->template = $this->stylesheet; |
241 $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); |
251 $this->cache_add( |
|
252 'theme', |
|
253 array( |
|
254 'headers' => $this->headers, |
|
255 'errors' => $this->errors, |
|
256 'stylesheet' => $this->stylesheet, |
|
257 'template' => $this->template, |
|
258 ) |
|
259 ); |
242 return; |
260 return; |
243 } else { |
261 } else { |
244 $this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' ); |
262 $this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' ); |
245 // Default themes always trump their pretenders. |
263 // Default themes always trump their pretenders. |
246 // Properly identify default themes that are inside a directory within wp-content/themes. |
264 // Properly identify default themes that are inside a directory within wp-content/themes. |
247 if ( $default_theme_slug = array_search( $this->headers['Name'], self::$default_themes ) ) { |
265 if ( $default_theme_slug = array_search( $this->headers['Name'], self::$default_themes ) ) { |
248 if ( basename( $this->stylesheet ) != $default_theme_slug ) |
266 if ( basename( $this->stylesheet ) != $default_theme_slug ) { |
249 $this->headers['Name'] .= '/' . $this->stylesheet; |
267 $this->headers['Name'] .= '/' . $this->stylesheet; |
|
268 } |
250 } |
269 } |
251 } |
270 } |
252 |
271 |
253 if ( ! $this->template && $this->stylesheet === $this->headers['Template'] ) { |
272 if ( ! $this->template && $this->stylesheet === $this->headers['Template'] ) { |
254 /* translators: %s: Template */ |
273 /* translators: %s: Template */ |
255 $this->errors = new WP_Error( 'theme_child_invalid', sprintf( __( 'The theme defines itself as its parent theme. Please check the %s header.' ), '<code>Template</code>' ) ); |
274 $this->errors = new WP_Error( 'theme_child_invalid', sprintf( __( 'The theme defines itself as its parent theme. Please check the %s header.' ), '<code>Template</code>' ) ); |
256 $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet ) ); |
275 $this->cache_add( |
|
276 'theme', |
|
277 array( |
|
278 'headers' => $this->headers, |
|
279 'errors' => $this->errors, |
|
280 'stylesheet' => $this->stylesheet, |
|
281 ) |
|
282 ); |
257 |
283 |
258 return; |
284 return; |
259 } |
285 } |
260 |
286 |
261 // (If template is set from cache [and there are no errors], we know it's good.) |
287 // (If template is set from cache [and there are no errors], we know it's good.) |
262 if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) { |
288 if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) { |
263 $this->template = $this->stylesheet; |
289 $this->template = $this->stylesheet; |
264 if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) { |
290 if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) { |
265 $error_message = sprintf( |
291 $error_message = sprintf( |
266 /* translators: 1: index.php, 2: Codex URL, 3: style.css */ |
292 /* translators: 1: index.php, 2: link to documentation, 3: style.css */ |
267 __( 'Template is missing. Standalone themes need to have a %1$s template file. <a href="%2$s">Child themes</a> need to have a Template header in the %3$s stylesheet.' ), |
293 __( 'Template is missing. Standalone themes need to have a %1$s template file. <a href="%2$s">Child themes</a> need to have a Template header in the %3$s stylesheet.' ), |
268 '<code>index.php</code>', |
294 '<code>index.php</code>', |
269 __( 'https://codex.wordpress.org/Child_Themes' ), |
295 __( 'https://developer.wordpress.org/themes/advanced-topics/child-themes/' ), |
270 '<code>style.css</code>' |
296 '<code>style.css</code>' |
271 ); |
297 ); |
272 $this->errors = new WP_Error( 'theme_no_index', $error_message ); |
298 $this->errors = new WP_Error( 'theme_no_index', $error_message ); |
273 $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); |
299 $this->cache_add( |
|
300 'theme', |
|
301 array( |
|
302 'headers' => $this->headers, |
|
303 'errors' => $this->errors, |
|
304 'stylesheet' => $this->stylesheet, |
|
305 'template' => $this->template, |
|
306 ) |
|
307 ); |
274 return; |
308 return; |
275 } |
309 } |
276 } |
310 } |
277 |
311 |
278 // If we got our data from cache, we can assume that 'template' is pointing to the right place. |
312 // If we got our data from cache, we can assume that 'template' is pointing to the right place. |
299 if ( $this->template != $this->stylesheet ) { |
341 if ( $this->template != $this->stylesheet ) { |
300 // If we are a parent, then there is a problem. Only two generations allowed! Cancel things out. |
342 // If we are a parent, then there is a problem. Only two generations allowed! Cancel things out. |
301 if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) { |
343 if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) { |
302 $_child->parent = null; |
344 $_child->parent = null; |
303 $_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $_child->template ) ) ); |
345 $_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $_child->template ) ) ); |
304 $_child->cache_add( 'theme', array( 'headers' => $_child->headers, 'errors' => $_child->errors, 'stylesheet' => $_child->stylesheet, 'template' => $_child->template ) ); |
346 $_child->cache_add( |
|
347 'theme', |
|
348 array( |
|
349 'headers' => $_child->headers, |
|
350 'errors' => $_child->errors, |
|
351 'stylesheet' => $_child->stylesheet, |
|
352 'template' => $_child->template, |
|
353 ) |
|
354 ); |
305 // The two themes actually reference each other with the Template header. |
355 // The two themes actually reference each other with the Template header. |
306 if ( $_child->stylesheet == $this->template ) { |
356 if ( $_child->stylesheet == $this->template ) { |
307 $this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $this->template ) ) ); |
357 $this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $this->template ) ) ); |
308 $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); |
358 $this->cache_add( |
|
359 'theme', |
|
360 array( |
|
361 'headers' => $this->headers, |
|
362 'errors' => $this->errors, |
|
363 'stylesheet' => $this->stylesheet, |
|
364 'template' => $this->template, |
|
365 ) |
|
366 ); |
309 } |
367 } |
310 return; |
368 return; |
311 } |
369 } |
312 // Set the parent. Pass the current instance so we can do the crazy checks above and assess errors. |
370 // Set the parent. Pass the current instance so we can do the crazy checks above and assess errors. |
313 $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this ); |
371 $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this ); |
314 } |
372 } |
315 |
373 |
|
374 if ( wp_paused_themes()->get( $this->stylesheet ) && ( ! is_wp_error( $this->errors ) || ! isset( $this->errors->errors['theme_paused'] ) ) ) { |
|
375 $this->errors = new WP_Error( 'theme_paused', __( 'This theme failed to load properly and was paused within the admin backend.' ) ); |
|
376 } |
|
377 |
316 // We're good. If we didn't retrieve from cache, set it. |
378 // We're good. If we didn't retrieve from cache, set it. |
317 if ( ! is_array( $cache ) ) { |
379 if ( ! is_array( $cache ) ) { |
318 $cache = array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ); |
380 $cache = array( |
|
381 'headers' => $this->headers, |
|
382 'errors' => $this->errors, |
|
383 'stylesheet' => $this->stylesheet, |
|
384 'template' => $this->template, |
|
385 ); |
319 // If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above. |
386 // If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above. |
320 if ( isset( $theme_root_template ) ) |
387 if ( isset( $theme_root_template ) ) { |
321 $cache['theme_root_template'] = $theme_root_template; |
388 $cache['theme_root_template'] = $theme_root_template; |
|
389 } |
322 $this->cache_add( 'theme', $cache ); |
390 $this->cache_add( 'theme', $cache ); |
323 } |
391 } |
324 } |
392 } |
325 |
393 |
326 /** |
394 /** |
361 * @param string $offset Property to get. |
441 * @param string $offset Property to get. |
362 * @return mixed Property value. |
442 * @return mixed Property value. |
363 */ |
443 */ |
364 public function __get( $offset ) { |
444 public function __get( $offset ) { |
365 switch ( $offset ) { |
445 switch ( $offset ) { |
366 case 'name' : |
446 case 'name': |
367 case 'title' : |
447 case 'title': |
368 return $this->get('Name'); |
448 return $this->get( 'Name' ); |
369 case 'version' : |
449 case 'version': |
370 return $this->get('Version'); |
450 return $this->get( 'Version' ); |
371 case 'parent_theme' : |
451 case 'parent_theme': |
372 return $this->parent() ? $this->parent()->get('Name') : ''; |
452 return $this->parent() ? $this->parent()->get( 'Name' ) : ''; |
373 case 'template_dir' : |
453 case 'template_dir': |
374 return $this->get_template_directory(); |
454 return $this->get_template_directory(); |
375 case 'stylesheet_dir' : |
455 case 'stylesheet_dir': |
376 return $this->get_stylesheet_directory(); |
456 return $this->get_stylesheet_directory(); |
377 case 'template' : |
457 case 'template': |
378 return $this->get_template(); |
458 return $this->get_template(); |
379 case 'stylesheet' : |
459 case 'stylesheet': |
380 return $this->get_stylesheet(); |
460 return $this->get_stylesheet(); |
381 case 'screenshot' : |
461 case 'screenshot': |
382 return $this->get_screenshot( 'relative' ); |
462 return $this->get_screenshot( 'relative' ); |
383 // 'author' and 'description' did not previously return translated data. |
463 // 'author' and 'description' did not previously return translated data. |
384 case 'description' : |
464 case 'description': |
385 return $this->display('Description'); |
465 return $this->display( 'Description' ); |
386 case 'author' : |
466 case 'author': |
387 return $this->display('Author'); |
467 return $this->display( 'Author' ); |
388 case 'tags' : |
468 case 'tags': |
389 return $this->get( 'Tags' ); |
469 return $this->get( 'Tags' ); |
390 case 'theme_root' : |
470 case 'theme_root': |
391 return $this->get_theme_root(); |
471 return $this->get_theme_root(); |
392 case 'theme_root_uri' : |
472 case 'theme_root_uri': |
393 return $this->get_theme_root_uri(); |
473 return $this->get_theme_root_uri(); |
394 // For cases where the array was converted to an object. |
474 // For cases where the array was converted to an object. |
395 default : |
475 default: |
396 return $this->offsetGet( $offset ); |
476 return $this->offsetGet( $offset ); |
397 } |
477 } |
398 } |
478 } |
399 |
479 |
400 /** |
480 /** |
451 * @param mixed $offset |
547 * @param mixed $offset |
452 * @return mixed |
548 * @return mixed |
453 */ |
549 */ |
454 public function offsetGet( $offset ) { |
550 public function offsetGet( $offset ) { |
455 switch ( $offset ) { |
551 switch ( $offset ) { |
456 case 'Name' : |
552 case 'Name': |
457 case 'Title' : |
553 case 'Title': |
458 /* |
554 /* |
459 * See note above about using translated data. get() is not ideal. |
555 * See note above about using translated data. get() is not ideal. |
460 * It is only for backward compatibility. Use display(). |
556 * It is only for backward compatibility. Use display(). |
461 */ |
557 */ |
462 return $this->get('Name'); |
558 return $this->get( 'Name' ); |
463 case 'Author' : |
559 case 'Author': |
464 return $this->display( 'Author'); |
560 return $this->display( 'Author' ); |
465 case 'Author Name' : |
561 case 'Author Name': |
466 return $this->display( 'Author', false); |
562 return $this->display( 'Author', false ); |
467 case 'Author URI' : |
563 case 'Author URI': |
468 return $this->display('AuthorURI'); |
564 return $this->display( 'AuthorURI' ); |
469 case 'Description' : |
565 case 'Description': |
470 return $this->display( 'Description'); |
566 return $this->display( 'Description' ); |
471 case 'Version' : |
567 case 'Version': |
472 case 'Status' : |
568 case 'Status': |
473 return $this->get( $offset ); |
569 return $this->get( $offset ); |
474 case 'Template' : |
570 case 'Template': |
475 return $this->get_template(); |
571 return $this->get_template(); |
476 case 'Stylesheet' : |
572 case 'Stylesheet': |
477 return $this->get_stylesheet(); |
573 return $this->get_stylesheet(); |
478 case 'Template Files' : |
574 case 'Template Files': |
479 return $this->get_files( 'php', 1, true ); |
575 return $this->get_files( 'php', 1, true ); |
480 case 'Stylesheet Files' : |
576 case 'Stylesheet Files': |
481 return $this->get_files( 'css', 0, false ); |
577 return $this->get_files( 'css', 0, false ); |
482 case 'Template Dir' : |
578 case 'Template Dir': |
483 return $this->get_template_directory(); |
579 return $this->get_template_directory(); |
484 case 'Stylesheet Dir' : |
580 case 'Stylesheet Dir': |
485 return $this->get_stylesheet_directory(); |
581 return $this->get_stylesheet_directory(); |
486 case 'Screenshot' : |
582 case 'Screenshot': |
487 return $this->get_screenshot( 'relative' ); |
583 return $this->get_screenshot( 'relative' ); |
488 case 'Tags' : |
584 case 'Tags': |
489 return $this->get('Tags'); |
585 return $this->get( 'Tags' ); |
490 case 'Theme Root' : |
586 case 'Theme Root': |
491 return $this->get_theme_root(); |
587 return $this->get_theme_root(); |
492 case 'Theme Root URI' : |
588 case 'Theme Root URI': |
493 return $this->get_theme_root_uri(); |
589 return $this->get_theme_root_uri(); |
494 case 'Parent Theme' : |
590 case 'Parent Theme': |
495 return $this->parent() ? $this->parent()->get('Name') : ''; |
591 return $this->parent() ? $this->parent()->get( 'Name' ) : ''; |
496 default : |
592 default: |
497 return null; |
593 return null; |
498 } |
594 } |
499 } |
595 } |
500 |
596 |
501 /** |
597 /** |
591 * |
688 * |
592 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. |
689 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. |
593 * @return string|false String on success, false on failure. |
690 * @return string|false String on success, false on failure. |
594 */ |
691 */ |
595 public function get( $header ) { |
692 public function get( $header ) { |
596 if ( ! isset( $this->headers[ $header ] ) ) |
693 if ( ! isset( $this->headers[ $header ] ) ) { |
597 return false; |
694 return false; |
|
695 } |
598 |
696 |
599 if ( ! isset( $this->headers_sanitized ) ) { |
697 if ( ! isset( $this->headers_sanitized ) ) { |
600 $this->headers_sanitized = $this->cache_get( 'headers' ); |
698 $this->headers_sanitized = $this->cache_get( 'headers' ); |
601 if ( ! is_array( $this->headers_sanitized ) ) |
699 if ( ! is_array( $this->headers_sanitized ) ) { |
602 $this->headers_sanitized = array(); |
700 $this->headers_sanitized = array(); |
603 } |
701 } |
604 |
702 } |
605 if ( isset( $this->headers_sanitized[ $header ] ) ) |
703 |
|
704 if ( isset( $this->headers_sanitized[ $header ] ) ) { |
606 return $this->headers_sanitized[ $header ]; |
705 return $this->headers_sanitized[ $header ]; |
|
706 } |
607 |
707 |
608 // If themes are a persistent group, sanitize everything and cache it. One cache add is better than many cache sets. |
708 // If themes are a persistent group, sanitize everything and cache it. One cache add is better than many cache sets. |
609 if ( self::$persistently_cache ) { |
709 if ( self::$persistently_cache ) { |
610 foreach ( array_keys( $this->headers ) as $_header ) |
710 foreach ( array_keys( $this->headers ) as $_header ) { |
611 $this->headers_sanitized[ $_header ] = $this->sanitize_header( $_header, $this->headers[ $_header ] ); |
711 $this->headers_sanitized[ $_header ] = $this->sanitize_header( $_header, $this->headers[ $_header ] ); |
|
712 } |
612 $this->cache_add( 'headers', $this->headers_sanitized ); |
713 $this->cache_add( 'headers', $this->headers_sanitized ); |
613 } else { |
714 } else { |
614 $this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] ); |
715 $this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] ); |
615 } |
716 } |
616 |
717 |
657 * @param string $value Value to sanitize. |
761 * @param string $value Value to sanitize. |
658 * @return mixed |
762 * @return mixed |
659 */ |
763 */ |
660 private function sanitize_header( $header, $value ) { |
764 private function sanitize_header( $header, $value ) { |
661 switch ( $header ) { |
765 switch ( $header ) { |
662 case 'Status' : |
766 case 'Status': |
663 if ( ! $value ) { |
767 if ( ! $value ) { |
664 $value = 'publish'; |
768 $value = 'publish'; |
665 break; |
769 break; |
666 } |
770 } |
667 // Fall through otherwise. |
771 // Fall through otherwise. |
668 case 'Name' : |
772 case 'Name': |
669 static $header_tags = array( |
773 static $header_tags = array( |
670 'abbr' => array( 'title' => true ), |
774 'abbr' => array( 'title' => true ), |
671 'acronym' => array( 'title' => true ), |
775 'acronym' => array( 'title' => true ), |
672 'code' => true, |
776 'code' => true, |
673 'em' => true, |
777 'em' => true, |
674 'strong' => true, |
778 'strong' => true, |
675 ); |
779 ); |
676 $value = wp_kses( $value, $header_tags ); |
780 $value = wp_kses( $value, $header_tags ); |
677 break; |
781 break; |
678 case 'Author' : |
782 case 'Author': |
679 // There shouldn't be anchor tags in Author, but some themes like to be challenging. |
783 // There shouldn't be anchor tags in Author, but some themes like to be challenging. |
680 case 'Description' : |
784 case 'Description': |
681 static $header_tags_with_a = array( |
785 static $header_tags_with_a = array( |
682 'a' => array( 'href' => true, 'title' => true ), |
786 'a' => array( |
|
787 'href' => true, |
|
788 'title' => true, |
|
789 ), |
683 'abbr' => array( 'title' => true ), |
790 'abbr' => array( 'title' => true ), |
684 'acronym' => array( 'title' => true ), |
791 'acronym' => array( 'title' => true ), |
685 'code' => true, |
792 'code' => true, |
686 'em' => true, |
793 'em' => true, |
687 'strong' => true, |
794 'strong' => true, |
688 ); |
795 ); |
689 $value = wp_kses( $value, $header_tags_with_a ); |
796 $value = wp_kses( $value, $header_tags_with_a ); |
690 break; |
797 break; |
691 case 'ThemeURI' : |
798 case 'ThemeURI': |
692 case 'AuthorURI' : |
799 case 'AuthorURI': |
693 $value = esc_url_raw( $value ); |
800 $value = esc_url_raw( $value ); |
694 break; |
801 break; |
695 case 'Tags' : |
802 case 'Tags': |
696 $value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) ); |
803 $value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) ); |
697 break; |
804 break; |
698 case 'Version' : |
805 case 'Version': |
699 $value = strip_tags( $value ); |
806 $value = strip_tags( $value ); |
700 break; |
807 break; |
701 } |
808 } |
702 |
809 |
703 return $value; |
810 return $value; |
704 } |
811 } |
705 |
812 |
706 /** |
813 /** |
707 * Mark up a theme header. |
814 * Mark up a theme header. |
708 * |
815 * |
709 * @since 3.4.0 |
816 * @since 3.4.0 |
710 * |
817 * |
711 * @staticvar string $comma |
818 * @staticvar string $comma |
712 * |
819 * |
713 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. |
820 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. |
714 * @param string $value Value to mark up. |
821 * @param string $value Value to mark up. |
715 * @param string $translate Whether the header has been translated. |
822 * @param string $translate Whether the header has been translated. |
716 * @return string Value, marked up. |
823 * @return string Value, marked up. |
717 */ |
824 */ |
718 private function markup_header( $header, $value, $translate ) { |
825 private function markup_header( $header, $value, $translate ) { |
719 switch ( $header ) { |
826 switch ( $header ) { |
720 case 'Name' : |
827 case 'Name': |
721 if ( empty( $value ) ) { |
828 if ( empty( $value ) ) { |
722 $value = esc_html( $this->get_stylesheet() ); |
829 $value = esc_html( $this->get_stylesheet() ); |
723 } |
830 } |
724 break; |
831 break; |
725 case 'Description' : |
832 case 'Description': |
726 $value = wptexturize( $value ); |
833 $value = wptexturize( $value ); |
727 break; |
834 break; |
728 case 'Author' : |
835 case 'Author': |
729 if ( $this->get('AuthorURI') ) { |
836 if ( $this->get( 'AuthorURI' ) ) { |
730 $value = sprintf( '<a href="%1$s">%2$s</a>', $this->display( 'AuthorURI', true, $translate ), $value ); |
837 $value = sprintf( '<a href="%1$s">%2$s</a>', $this->display( 'AuthorURI', true, $translate ), $value ); |
731 } elseif ( ! $value ) { |
838 } elseif ( ! $value ) { |
732 $value = __( 'Anonymous' ); |
839 $value = __( 'Anonymous' ); |
733 } |
840 } |
734 break; |
841 break; |
735 case 'Tags' : |
842 case 'Tags': |
736 static $comma = null; |
843 static $comma = null; |
737 if ( ! isset( $comma ) ) { |
844 if ( ! isset( $comma ) ) { |
738 /* translators: used between list items, there is a space after the comma */ |
845 /* translators: used between list items, there is a space after the comma */ |
739 $comma = __( ', ' ); |
846 $comma = __( ', ' ); |
740 } |
847 } |
741 $value = implode( $comma, $value ); |
848 $value = implode( $comma, $value ); |
742 break; |
849 break; |
743 case 'ThemeURI' : |
850 case 'ThemeURI': |
744 case 'AuthorURI' : |
851 case 'AuthorURI': |
745 $value = esc_url( $value ); |
852 $value = esc_url( $value ); |
746 break; |
853 break; |
747 } |
854 } |
748 |
855 |
749 return $value; |
856 return $value; |
760 * @param string $value Value to translate. |
867 * @param string $value Value to translate. |
761 * @return string Translated value. |
868 * @return string Translated value. |
762 */ |
869 */ |
763 private function translate_header( $header, $value ) { |
870 private function translate_header( $header, $value ) { |
764 switch ( $header ) { |
871 switch ( $header ) { |
765 case 'Name' : |
872 case 'Name': |
766 // Cached for sorting reasons. |
873 // Cached for sorting reasons. |
767 if ( isset( $this->name_translated ) ) |
874 if ( isset( $this->name_translated ) ) { |
768 return $this->name_translated; |
875 return $this->name_translated; |
769 $this->name_translated = translate( $value, $this->get('TextDomain' ) ); |
876 } |
|
877 // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain |
|
878 $this->name_translated = translate( $value, $this->get( 'TextDomain' ) ); |
770 return $this->name_translated; |
879 return $this->name_translated; |
771 case 'Tags' : |
880 case 'Tags': |
772 if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) ) { |
881 if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) ) { |
773 return $value; |
882 return $value; |
774 } |
883 } |
775 |
884 |
776 static $tags_list; |
885 static $tags_list; |
777 if ( ! isset( $tags_list ) ) { |
886 if ( ! isset( $tags_list ) ) { |
778 $tags_list = array( |
887 $tags_list = array( |
779 // As of 4.6, deprecated tags which are only used to provide translation for older themes. |
888 // As of 4.6, deprecated tags which are only used to provide translation for older themes. |
780 'black' => __( 'Black' ), 'blue' => __( 'Blue' ), 'brown' => __( 'Brown' ), |
889 'black' => __( 'Black' ), |
781 'gray' => __( 'Gray' ), 'green' => __( 'Green' ), 'orange' => __( 'Orange' ), |
890 'blue' => __( 'Blue' ), |
782 'pink' => __( 'Pink' ), 'purple' => __( 'Purple' ), 'red' => __( 'Red' ), |
891 'brown' => __( 'Brown' ), |
783 'silver' => __( 'Silver' ), 'tan' => __( 'Tan' ), 'white' => __( 'White' ), |
892 'gray' => __( 'Gray' ), |
784 'yellow' => __( 'Yellow' ), 'dark' => __( 'Dark' ), 'light' => __( 'Light' ), |
893 'green' => __( 'Green' ), |
785 'fixed-layout' => __( 'Fixed Layout' ), 'fluid-layout' => __( 'Fluid Layout' ), |
894 'orange' => __( 'Orange' ), |
786 'responsive-layout' => __( 'Responsive Layout' ), 'blavatar' => __( 'Blavatar' ), |
895 'pink' => __( 'Pink' ), |
787 'photoblogging' => __( 'Photoblogging' ), 'seasonal' => __( 'Seasonal' ), |
896 'purple' => __( 'Purple' ), |
|
897 'red' => __( 'Red' ), |
|
898 'silver' => __( 'Silver' ), |
|
899 'tan' => __( 'Tan' ), |
|
900 'white' => __( 'White' ), |
|
901 'yellow' => __( 'Yellow' ), |
|
902 'dark' => __( 'Dark' ), |
|
903 'light' => __( 'Light' ), |
|
904 'fixed-layout' => __( 'Fixed Layout' ), |
|
905 'fluid-layout' => __( 'Fluid Layout' ), |
|
906 'responsive-layout' => __( 'Responsive Layout' ), |
|
907 'blavatar' => __( 'Blavatar' ), |
|
908 'photoblogging' => __( 'Photoblogging' ), |
|
909 'seasonal' => __( 'Seasonal' ), |
788 ); |
910 ); |
789 |
911 |
790 $feature_list = get_theme_feature_list( false ); // No API |
912 $feature_list = get_theme_feature_list( false ); // No API |
791 foreach ( $feature_list as $tags ) { |
913 foreach ( $feature_list as $tags ) { |
792 $tags_list += $tags; |
914 $tags_list += $tags; |
950 * @return string|false Screenshot file. False if the theme does not have a screenshot. |
1077 * @return string|false Screenshot file. False if the theme does not have a screenshot. |
951 */ |
1078 */ |
952 public function get_screenshot( $uri = 'uri' ) { |
1079 public function get_screenshot( $uri = 'uri' ) { |
953 $screenshot = $this->cache_get( 'screenshot' ); |
1080 $screenshot = $this->cache_get( 'screenshot' ); |
954 if ( $screenshot ) { |
1081 if ( $screenshot ) { |
955 if ( 'relative' == $uri ) |
1082 if ( 'relative' == $uri ) { |
956 return $screenshot; |
1083 return $screenshot; |
|
1084 } |
957 return $this->get_stylesheet_directory_uri() . '/' . $screenshot; |
1085 return $this->get_stylesheet_directory_uri() . '/' . $screenshot; |
958 } elseif ( 0 === $screenshot ) { |
1086 } elseif ( 0 === $screenshot ) { |
959 return false; |
1087 return false; |
960 } |
1088 } |
961 |
1089 |
962 foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) { |
1090 foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) { |
963 if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) { |
1091 if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) { |
964 $this->cache_add( 'screenshot', 'screenshot.' . $ext ); |
1092 $this->cache_add( 'screenshot', 'screenshot.' . $ext ); |
965 if ( 'relative' == $uri ) |
1093 if ( 'relative' == $uri ) { |
966 return 'screenshot.' . $ext; |
1094 return 'screenshot.' . $ext; |
|
1095 } |
967 return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext; |
1096 return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext; |
968 } |
1097 } |
969 } |
1098 } |
970 |
1099 |
971 $this->cache_add( 'screenshot', 0 ); |
1100 $this->cache_add( 'screenshot', 0 ); |
1122 if ( ! is_dir( $path ) ) { |
1249 if ( ! is_dir( $path ) ) { |
1123 return false; |
1250 return false; |
1124 } |
1251 } |
1125 |
1252 |
1126 if ( $extensions ) { |
1253 if ( $extensions ) { |
1127 $extensions = (array) $extensions; |
1254 $extensions = (array) $extensions; |
1128 $_extensions = implode( '|', $extensions ); |
1255 $_extensions = implode( '|', $extensions ); |
1129 } |
1256 } |
1130 |
1257 |
1131 $relative_path = trailingslashit( $relative_path ); |
1258 $relative_path = trailingslashit( $relative_path ); |
1132 if ( '/' == $relative_path ) { |
1259 if ( '/' == $relative_path ) { |
1133 $relative_path = ''; |
1260 $relative_path = ''; |
1134 } |
1261 } |
1135 |
1262 |
1136 $results = scandir( $path ); |
1263 $results = scandir( $path ); |
1137 $files = array(); |
1264 $files = array(); |
1138 |
1265 |
1139 /** |
1266 /** |
1140 * Filters the array of excluded directories and files while scanning theme folder. |
1267 * Filters the array of excluded directories and files while scanning theme folder. |
1141 * |
1268 * |
1142 * @since 4.7.4 |
1269 * @since 4.7.4 |
1143 * |
1270 * |
1144 * @param array $exclusions Array of excluded directories and files. |
1271 * @param string[] $exclusions Array of excluded directories and files. |
1145 */ |
1272 */ |
1146 $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) ); |
1273 $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) ); |
1147 |
1274 |
1148 foreach ( $results as $result ) { |
1275 foreach ( $results as $result ) { |
1149 if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) { |
1276 if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) { |
1250 /** |
1382 /** |
1251 * Returns array of stylesheet names of themes allowed on the site or network. |
1383 * Returns array of stylesheet names of themes allowed on the site or network. |
1252 * |
1384 * |
1253 * @since 3.4.0 |
1385 * @since 3.4.0 |
1254 * |
1386 * |
1255 * @static |
|
1256 * |
|
1257 * @param int $blog_id Optional. ID of the site. Defaults to the current site. |
1387 * @param int $blog_id Optional. ID of the site. Defaults to the current site. |
1258 * @return array Array of stylesheet names. |
1388 * @return string[] Array of stylesheet names. |
1259 */ |
1389 */ |
1260 public static function get_allowed( $blog_id = null ) { |
1390 public static function get_allowed( $blog_id = null ) { |
1261 /** |
1391 /** |
1262 * Filters the array of themes allowed on the network. |
1392 * Filters the array of themes allowed on the network. |
1263 * |
1393 * |
1264 * Site is provided as context so that a list of network allowed themes can |
1394 * Site is provided as context so that a list of network allowed themes can |
1265 * be filtered further. |
1395 * be filtered further. |
1266 * |
1396 * |
1267 * @since 4.5.0 |
1397 * @since 4.5.0 |
1268 * |
1398 * |
1269 * @param array $allowed_themes An array of theme stylesheet names. |
1399 * @param string[] $allowed_themes An array of theme stylesheet names. |
1270 * @param int $blog_id ID of the site. |
1400 * @param int $blog_id ID of the site. |
1271 */ |
1401 */ |
1272 $network = (array) apply_filters( 'network_allowed_themes', self::get_allowed_on_network(), $blog_id ); |
1402 $network = (array) apply_filters( 'network_allowed_themes', self::get_allowed_on_network(), $blog_id ); |
1273 return $network + self::get_allowed_on_site( $blog_id ); |
1403 return $network + self::get_allowed_on_site( $blog_id ); |
1274 } |
1404 } |
1275 |
1405 |
1276 /** |
1406 /** |
1277 * Returns array of stylesheet names of themes allowed on the network. |
1407 * Returns array of stylesheet names of themes allowed on the network. |
1278 * |
1408 * |
1279 * @since 3.4.0 |
1409 * @since 3.4.0 |
1280 * |
1410 * |
1281 * @static |
|
1282 * |
|
1283 * @staticvar array $allowed_themes |
1411 * @staticvar array $allowed_themes |
1284 * |
1412 * |
1285 * @return array Array of stylesheet names. |
1413 * @return string[] Array of stylesheet names. |
1286 */ |
1414 */ |
1287 public static function get_allowed_on_network() { |
1415 public static function get_allowed_on_network() { |
1288 static $allowed_themes; |
1416 static $allowed_themes; |
1289 if ( ! isset( $allowed_themes ) ) { |
1417 if ( ! isset( $allowed_themes ) ) { |
1290 $allowed_themes = (array) get_site_option( 'allowedthemes' ); |
1418 $allowed_themes = (array) get_site_option( 'allowedthemes' ); |