11 * |
11 * |
12 * Despite advances over get_themes(), this function is quite expensive, and grows |
12 * Despite advances over get_themes(), this function is quite expensive, and grows |
13 * linearly with additional themes. Stick to wp_get_theme() if possible. |
13 * linearly with additional themes. Stick to wp_get_theme() if possible. |
14 * |
14 * |
15 * @since 3.4.0 |
15 * @since 3.4.0 |
|
16 * |
|
17 * @global array $wp_theme_directories |
|
18 * @staticvar array $_themes |
16 * |
19 * |
17 * @param array $args The search arguments. Optional. |
20 * @param array $args The search arguments. Optional. |
18 * - errors mixed True to return themes with errors, false to return themes without errors, null |
21 * - errors mixed True to return themes with errors, false to return themes without errors, null |
19 * to return all themes. Defaults to false. |
22 * to return all themes. Defaults to false. |
20 * - allowed mixed (Multisite) True to return only allowed themes for a site. False to return only |
23 * - allowed mixed (Multisite) True to return only allowed themes for a site. False to return only |
21 * disallowed themes for a site. 'site' to return only site-allowed themes. 'network' |
24 * disallowed themes for a site. 'site' to return only site-allowed themes. 'network' |
22 * to return only network-allowed themes. Null to return all themes. Defaults to null. |
25 * to return only network-allowed themes. Null to return all themes. Defaults to null. |
23 * - blog_id int (Multisite) The blog ID used to calculate which themes are allowed. Defaults to 0, |
26 * - blog_id int (Multisite) The blog ID used to calculate which themes are allowed. Defaults to 0, |
24 * synonymous for the current blog. |
27 * synonymous for the current blog. |
25 * @return Array of WP_Theme objects. |
28 * @return array Array of WP_Theme objects. |
26 */ |
29 */ |
27 function wp_get_themes( $args = array() ) { |
30 function wp_get_themes( $args = array() ) { |
28 global $wp_theme_directories; |
31 global $wp_theme_directories; |
29 |
32 |
30 $defaults = array( 'errors' => false, 'allowed' => null, 'blog_id' => 0 ); |
33 $defaults = array( 'errors' => false, 'allowed' => null, 'blog_id' => 0 ); |
31 $args = wp_parse_args( $args, $defaults ); |
34 $args = wp_parse_args( $args, $defaults ); |
32 |
35 |
33 $theme_directories = search_theme_directories(); |
36 $theme_directories = search_theme_directories(); |
34 |
37 |
35 if ( count( $wp_theme_directories ) > 1 ) { |
38 if ( is_array( $wp_theme_directories ) && count( $wp_theme_directories ) > 1 ) { |
36 // Make sure the current theme wins out, in case search_theme_directories() picks the wrong |
39 // Make sure the current theme wins out, in case search_theme_directories() picks the wrong |
37 // one in the case of a conflict. (Normally, last registered theme root wins.) |
40 // one in the case of a conflict. (Normally, last registered theme root wins.) |
38 $current_theme = get_stylesheet(); |
41 $current_theme = get_stylesheet(); |
39 if ( isset( $theme_directories[ $current_theme ] ) ) { |
42 if ( isset( $theme_directories[ $current_theme ] ) ) { |
40 $root_of_current_theme = get_raw_theme_root( $current_theme ); |
43 $root_of_current_theme = get_raw_theme_root( $current_theme ); |
202 */ |
207 */ |
203 return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri ); |
208 return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri ); |
204 } |
209 } |
205 |
210 |
206 /** |
211 /** |
207 * Retrieve URI of current theme stylesheet. |
212 * Retrieves the URI of current theme stylesheet. |
208 * |
213 * |
209 * The stylesheet file name is 'style.css' which is appended to {@link |
214 * The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path. |
210 * get_stylesheet_directory_uri() stylesheet directory URI} path. |
215 * See get_stylesheet_directory_uri(). |
211 * |
216 * |
212 * @since 1.5.0 |
217 * @since 1.5.0 |
213 * |
218 * |
214 * @return string |
219 * @return string |
215 */ |
220 */ |
216 function get_stylesheet_uri() { |
221 function get_stylesheet_uri() { |
217 $stylesheet_dir_uri = get_stylesheet_directory_uri(); |
222 $stylesheet_dir_uri = get_stylesheet_directory_uri(); |
218 $stylesheet_uri = $stylesheet_dir_uri . '/style.css'; |
223 $stylesheet_uri = $stylesheet_dir_uri . '/style.css'; |
219 /** |
224 /** |
220 * Filter the URI of the current theme stylesheet. |
225 * Filters the URI of the current theme stylesheet. |
221 * |
226 * |
222 * @since 1.5.0 |
227 * @since 1.5.0 |
223 * |
228 * |
224 * @param string $stylesheet_uri Stylesheet URI for the current theme/child theme. |
229 * @param string $stylesheet_uri Stylesheet URI for the current theme/child theme. |
225 * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme. |
230 * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme. |
226 */ |
231 */ |
227 return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri ); |
232 return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri ); |
228 } |
233 } |
229 |
234 |
230 /** |
235 /** |
231 * Retrieve localized stylesheet URI. |
236 * Retrieves the localized stylesheet URI. |
232 * |
237 * |
233 * The stylesheet directory for the localized stylesheet files are located, by |
238 * The stylesheet directory for the localized stylesheet files are located, by |
234 * default, in the base theme directory. The name of the locale file will be the |
239 * default, in the base theme directory. The name of the locale file will be the |
235 * locale followed by '.css'. If that does not exist, then the text direction |
240 * locale followed by '.css'. If that does not exist, then the text direction |
236 * stylesheet will be checked for existence, for example 'ltr.css'. |
241 * stylesheet will be checked for existence, for example 'ltr.css'. |
237 * |
242 * |
238 * The theme may change the location of the stylesheet directory by either using |
243 * The theme may change the location of the stylesheet directory by either using |
239 * the 'stylesheet_directory_uri' filter or the 'locale_stylesheet_uri' filter. |
244 * the {@see 'stylesheet_directory_uri'} or {@see 'locale_stylesheet_uri'} filters. |
|
245 * |
240 * If you want to change the location of the stylesheet files for the entire |
246 * If you want to change the location of the stylesheet files for the entire |
241 * WordPress workflow, then change the former. If you just have the locale in a |
247 * WordPress workflow, then change the former. If you just have the locale in a |
242 * separate folder, then change the latter. |
248 * separate folder, then change the latter. |
243 * |
249 * |
244 * @since 2.1.0 |
250 * @since 2.1.0 |
|
251 * |
|
252 * @global WP_Locale $wp_locale |
245 * |
253 * |
246 * @return string |
254 * @return string |
247 */ |
255 */ |
248 function get_locale_stylesheet_uri() { |
256 function get_locale_stylesheet_uri() { |
249 global $wp_locale; |
257 global $wp_locale; |
637 return; |
662 return; |
638 echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />'; |
663 echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />'; |
639 } |
664 } |
640 |
665 |
641 /** |
666 /** |
642 * Start preview theme output buffer. |
|
643 * |
|
644 * Will only perform task if the user has permissions and template and preview |
|
645 * query variables exist. |
|
646 * |
|
647 * @since 2.6.0 |
|
648 */ |
|
649 function preview_theme() { |
|
650 if ( ! (isset($_GET['template']) && isset($_GET['preview'])) ) |
|
651 return; |
|
652 |
|
653 if ( !current_user_can( 'switch_themes' ) ) |
|
654 return; |
|
655 |
|
656 // Admin Thickbox requests |
|
657 if ( isset( $_GET['preview_iframe'] ) ) |
|
658 show_admin_bar( false ); |
|
659 |
|
660 $_GET['template'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['template']); |
|
661 |
|
662 if ( validate_file($_GET['template']) ) |
|
663 return; |
|
664 |
|
665 add_filter( 'template', '_preview_theme_template_filter' ); |
|
666 |
|
667 if ( isset($_GET['stylesheet']) ) { |
|
668 $_GET['stylesheet'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['stylesheet']); |
|
669 if ( validate_file($_GET['stylesheet']) ) |
|
670 return; |
|
671 add_filter( 'stylesheet', '_preview_theme_stylesheet_filter' ); |
|
672 } |
|
673 |
|
674 // Prevent theme mods to current theme being used on theme being previewed |
|
675 add_filter( 'pre_option_theme_mods_' . get_option( 'stylesheet' ), '__return_empty_array' ); |
|
676 |
|
677 ob_start( 'preview_theme_ob_filter' ); |
|
678 } |
|
679 |
|
680 /** |
|
681 * Private function to modify the current template when previewing a theme |
|
682 * |
|
683 * @since 2.9.0 |
|
684 * @access private |
|
685 * |
|
686 * @return string |
|
687 */ |
|
688 function _preview_theme_template_filter() { |
|
689 return isset($_GET['template']) ? $_GET['template'] : ''; |
|
690 } |
|
691 |
|
692 /** |
|
693 * Private function to modify the current stylesheet when previewing a theme |
|
694 * |
|
695 * @since 2.9.0 |
|
696 * @access private |
|
697 * |
|
698 * @return string |
|
699 */ |
|
700 function _preview_theme_stylesheet_filter() { |
|
701 return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : ''; |
|
702 } |
|
703 |
|
704 /** |
|
705 * Callback function for ob_start() to capture all links in the theme. |
|
706 * |
|
707 * @since 2.6.0 |
|
708 * @access private |
|
709 * |
|
710 * @param string $content |
|
711 * @return string |
|
712 */ |
|
713 function preview_theme_ob_filter( $content ) { |
|
714 return preg_replace_callback( "|(<a.*?href=([\"']))(.*?)([\"'].*?>)|", 'preview_theme_ob_filter_callback', $content ); |
|
715 } |
|
716 |
|
717 /** |
|
718 * Manipulates preview theme links in order to control and maintain location. |
|
719 * |
|
720 * Callback function for preg_replace_callback() to accept and filter matches. |
|
721 * |
|
722 * @since 2.6.0 |
|
723 * @access private |
|
724 * |
|
725 * @param array $matches |
|
726 * @return string |
|
727 */ |
|
728 function preview_theme_ob_filter_callback( $matches ) { |
|
729 if ( strpos($matches[4], 'onclick') !== false ) |
|
730 $matches[4] = preg_replace('#onclick=([\'"]).*?(?<!\\\)\\1#i', '', $matches[4]); //Strip out any onclicks from rest of <a>. (?<!\\\) means to ignore the '" if it's escaped by \ to prevent breaking mid-attribute. |
|
731 if ( |
|
732 ( false !== strpos($matches[3], '/wp-admin/') ) |
|
733 || |
|
734 ( false !== strpos( $matches[3], '://' ) && 0 !== strpos( $matches[3], home_url() ) ) |
|
735 || |
|
736 ( false !== strpos($matches[3], '/feed/') ) |
|
737 || |
|
738 ( false !== strpos($matches[3], '/trackback/') ) |
|
739 ) |
|
740 return $matches[1] . "#$matches[2] onclick=$matches[2]return false;" . $matches[4]; |
|
741 |
|
742 $stylesheet = isset( $_GET['stylesheet'] ) ? $_GET['stylesheet'] : ''; |
|
743 $template = isset( $_GET['template'] ) ? $_GET['template'] : ''; |
|
744 |
|
745 $link = add_query_arg( array( 'preview' => 1, 'template' => $template, 'stylesheet' => $stylesheet, 'preview_iframe' => 1 ), $matches[3] ); |
|
746 if ( 0 === strpos($link, 'preview=1') ) |
|
747 $link = "?$link"; |
|
748 return $matches[1] . esc_attr( $link ) . $matches[4]; |
|
749 } |
|
750 |
|
751 /** |
|
752 * Switches the theme. |
667 * Switches the theme. |
753 * |
668 * |
754 * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature |
669 * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature |
755 * of two arguments: $template then $stylesheet. This is for backwards compatibility. |
670 * of two arguments: $template then $stylesheet. This is for backward compatibility. |
756 * |
671 * |
757 * @since 2.5.0 |
672 * @since 2.5.0 |
|
673 * |
|
674 * @global array $wp_theme_directories |
|
675 * @global WP_Customize_Manager $wp_customize |
|
676 * @global array $sidebars_widgets |
758 * |
677 * |
759 * @param string $stylesheet Stylesheet name |
678 * @param string $stylesheet Stylesheet name |
760 */ |
679 */ |
761 function switch_theme( $stylesheet ) { |
680 function switch_theme( $stylesheet ) { |
762 global $wp_theme_directories, $wp_customize, $sidebars_widgets; |
681 global $wp_theme_directories, $wp_customize, $sidebars_widgets; |
763 |
682 |
764 $_sidebars_widgets = null; |
683 $_sidebars_widgets = null; |
765 if ( 'wp_ajax_customize_save' === current_action() ) { |
684 if ( 'wp_ajax_customize_save' === current_action() ) { |
766 $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) ); |
685 $old_sidebars_widgets_data_setting = $wp_customize->get_setting( 'old_sidebars_widgets_data' ); |
|
686 if ( $old_sidebars_widgets_data_setting ) { |
|
687 $_sidebars_widgets = $wp_customize->post_value( $old_sidebars_widgets_data_setting ); |
|
688 } |
767 } elseif ( is_array( $sidebars_widgets ) ) { |
689 } elseif ( is_array( $sidebars_widgets ) ) { |
768 $_sidebars_widgets = $sidebars_widgets; |
690 $_sidebars_widgets = $sidebars_widgets; |
769 } |
691 } |
770 |
692 |
771 if ( is_array( $_sidebars_widgets ) ) { |
693 if ( is_array( $_sidebars_widgets ) ) { |
772 set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) ); |
694 set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) ); |
773 } |
695 } |
774 |
696 |
775 $old_theme = wp_get_theme(); |
697 $nav_menu_locations = get_theme_mod( 'nav_menu_locations' ); |
|
698 update_option( 'theme_switch_menu_locations', $nav_menu_locations ); |
|
699 |
|
700 if ( func_num_args() > 1 ) { |
|
701 $stylesheet = func_get_arg( 1 ); |
|
702 } |
|
703 |
|
704 $old_theme = wp_get_theme(); |
776 $new_theme = wp_get_theme( $stylesheet ); |
705 $new_theme = wp_get_theme( $stylesheet ); |
777 |
706 $template = $new_theme->get_template(); |
778 if ( func_num_args() > 1 ) { |
|
779 $template = $stylesheet; |
|
780 $stylesheet = func_get_arg( 1 ); |
|
781 } else { |
|
782 $template = $new_theme->get_template(); |
|
783 } |
|
784 |
707 |
785 update_option( 'template', $template ); |
708 update_option( 'template', $template ); |
786 update_option( 'stylesheet', $stylesheet ); |
709 update_option( 'stylesheet', $stylesheet ); |
787 |
710 |
788 if ( count( $wp_theme_directories ) > 1 ) { |
711 if ( count( $wp_theme_directories ) > 1 ) { |
798 update_option( 'current_theme', $new_name ); |
721 update_option( 'current_theme', $new_name ); |
799 |
722 |
800 // Migrate from the old mods_{name} option to theme_mods_{slug}. |
723 // Migrate from the old mods_{name} option to theme_mods_{slug}. |
801 if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) { |
724 if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) { |
802 $default_theme_mods = (array) get_option( 'mods_' . $new_name ); |
725 $default_theme_mods = (array) get_option( 'mods_' . $new_name ); |
|
726 if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) { |
|
727 $default_theme_mods['nav_menu_locations'] = $nav_menu_locations; |
|
728 } |
803 add_option( "theme_mods_$stylesheet", $default_theme_mods ); |
729 add_option( "theme_mods_$stylesheet", $default_theme_mods ); |
804 } else { |
730 } else { |
805 /* |
731 /* |
806 * Since retrieve_widgets() is called when initializing a theme in the Customizer, |
732 * Since retrieve_widgets() is called when initializing a theme in the Customizer, |
807 * we need to to remove the theme mods to avoid overwriting changes made via |
733 * we need to remove the theme mods to avoid overwriting changes made via |
808 * the Customizer when accessing wp-admin/widgets.php. |
734 * the Customizer when accessing wp-admin/widgets.php. |
809 */ |
735 */ |
810 if ( 'wp_ajax_customize_save' === current_action() ) { |
736 if ( 'wp_ajax_customize_save' === current_action() ) { |
811 remove_theme_mod( 'sidebars_widgets' ); |
737 remove_theme_mod( 'sidebars_widgets' ); |
812 } |
738 } |
813 } |
739 } |
814 |
740 |
815 update_option( 'theme_switched', $old_theme->get_stylesheet() ); |
741 update_option( 'theme_switched', $old_theme->get_stylesheet() ); |
|
742 |
816 /** |
743 /** |
817 * Fires after the theme is switched. |
744 * Fires after the theme is switched. |
818 * |
745 * |
819 * @since 1.5.0 |
746 * @since 1.5.0 |
|
747 * @since 4.5.0 Introduced the `$old_theme` parameter. |
820 * |
748 * |
821 * @param string $new_name Name of the new theme. |
749 * @param string $new_name Name of the new theme. |
822 * @param WP_Theme $new_theme WP_Theme instance of the new theme. |
750 * @param WP_Theme $new_theme WP_Theme instance of the new theme. |
|
751 * @param WP_Theme $old_theme WP_Theme instance of the old theme. |
823 */ |
752 */ |
824 do_action( 'switch_theme', $new_name, $new_theme ); |
753 do_action( 'switch_theme', $new_name, $new_theme, $old_theme ); |
825 } |
754 } |
826 |
755 |
827 /** |
756 /** |
828 * Checks that current theme files 'index.php' and 'style.css' exists. |
757 * Checks that current theme files 'index.php' and 'style.css' exists. |
829 * |
758 * |
830 * Does not check the default theme, which is the fallback and should always exist. |
759 * Does not initially check the default theme, which is the fallback and should always exist. |
|
760 * But if it doesn't exist, it'll fall back to the latest core default theme that does exist. |
831 * Will switch theme to the fallback theme if current theme does not validate. |
761 * Will switch theme to the fallback theme if current theme does not validate. |
832 * You can use the 'validate_current_theme' filter to return false to |
762 * |
|
763 * You can use the {@see 'validate_current_theme'} filter to return false to |
833 * disable this functionality. |
764 * disable this functionality. |
834 * |
765 * |
835 * @since 1.5.0 |
766 * @since 1.5.0 |
836 * @see WP_DEFAULT_THEME |
767 * @see WP_DEFAULT_THEME |
837 * |
768 * |
838 * @return bool |
769 * @return bool |
839 */ |
770 */ |
840 function validate_current_theme() { |
771 function validate_current_theme() { |
841 /** |
772 /** |
842 * Filter whether to validate the current theme. |
773 * Filters whether to validate the current theme. |
843 * |
774 * |
844 * @since 2.7.0 |
775 * @since 2.7.0 |
845 * |
776 * |
846 * @param bool true Validation flag to check the current theme. |
777 * @param bool $validate Whether to validate the current theme. Default true. |
847 */ |
778 */ |
848 if ( defined('WP_INSTALLING') || ! apply_filters( 'validate_current_theme', true ) ) |
779 if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) ) |
849 return true; |
780 return true; |
850 |
781 |
851 if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) { |
782 if ( ! file_exists( get_template_directory() . '/index.php' ) ) { |
|
783 // Invalid. |
|
784 } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) { |
|
785 // Invalid. |
|
786 } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) { |
|
787 // Invalid. |
|
788 } else { |
|
789 // Valid. |
|
790 return true; |
|
791 } |
|
792 |
|
793 $default = wp_get_theme( WP_DEFAULT_THEME ); |
|
794 if ( $default->exists() ) { |
852 switch_theme( WP_DEFAULT_THEME ); |
795 switch_theme( WP_DEFAULT_THEME ); |
853 return false; |
796 return false; |
854 } |
797 } |
855 |
798 |
856 if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) { |
799 /** |
857 switch_theme( WP_DEFAULT_THEME ); |
800 * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist, |
858 return false; |
801 * switch to the latest core default theme that's installed. |
859 } |
802 * If it turns out that this latest core default theme is our current |
860 |
803 * theme, then there's nothing we can do about that, so we have to bail, |
861 if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) { |
804 * rather than going into an infinite loop. (This is why there are |
862 switch_theme( WP_DEFAULT_THEME ); |
805 * checks against WP_DEFAULT_THEME above, also.) We also can't do anything |
863 return false; |
806 * if it turns out there is no default theme installed. (That's `false`.) |
864 } |
807 */ |
865 |
808 $default = WP_Theme::get_core_default_theme(); |
866 return true; |
809 if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) { |
|
810 return true; |
|
811 } |
|
812 |
|
813 switch_theme( $default->get_stylesheet() ); |
|
814 return false; |
867 } |
815 } |
868 |
816 |
869 /** |
817 /** |
870 * Retrieve all theme modifications. |
818 * Retrieve all theme modifications. |
871 * |
819 * |
872 * @since 3.1.0 |
820 * @since 3.1.0 |
873 * |
821 * |
874 * @return array|null Theme modifications. |
822 * @return array|void Theme modifications. |
875 */ |
823 */ |
876 function get_theme_mods() { |
824 function get_theme_mods() { |
877 $theme_slug = get_option( 'stylesheet' ); |
825 $theme_slug = get_option( 'stylesheet' ); |
878 if ( false === ( $mods = get_option( "theme_mods_$theme_slug" ) ) ) { |
826 $mods = get_option( "theme_mods_$theme_slug" ); |
|
827 if ( false === $mods ) { |
879 $theme_name = get_option( 'current_theme' ); |
828 $theme_name = get_option( 'current_theme' ); |
880 if ( false === $theme_name ) |
829 if ( false === $theme_name ) |
881 $theme_name = wp_get_theme()->get('Name'); |
830 $theme_name = wp_get_theme()->get('Name'); |
882 $mods = get_option( "mods_$theme_name" ); // Deprecated location. |
831 $mods = get_option( "mods_$theme_name" ); // Deprecated location. |
883 if ( is_admin() && false !== $mods ) { |
832 if ( is_admin() && false !== $mods ) { |
1065 |
1014 |
1066 return esc_url_raw( set_url_scheme( $url ) ); |
1015 return esc_url_raw( set_url_scheme( $url ) ); |
1067 } |
1016 } |
1068 |
1017 |
1069 /** |
1018 /** |
|
1019 * Create image tag markup for a custom header image. |
|
1020 * |
|
1021 * @since 4.4.0 |
|
1022 * |
|
1023 * @param array $attr Optional. Additional attributes for the image tag. Can be used |
|
1024 * to override the default attributes. Default empty. |
|
1025 * @return string HTML image element markup or empty string on failure. |
|
1026 */ |
|
1027 function get_header_image_tag( $attr = array() ) { |
|
1028 $header = get_custom_header(); |
|
1029 $header->url = get_header_image(); |
|
1030 |
|
1031 if ( ! $header->url ) { |
|
1032 return ''; |
|
1033 } |
|
1034 |
|
1035 $width = absint( $header->width ); |
|
1036 $height = absint( $header->height ); |
|
1037 |
|
1038 $attr = wp_parse_args( |
|
1039 $attr, |
|
1040 array( |
|
1041 'src' => $header->url, |
|
1042 'width' => $width, |
|
1043 'height' => $height, |
|
1044 'alt' => get_bloginfo( 'name' ), |
|
1045 ) |
|
1046 ); |
|
1047 |
|
1048 // Generate 'srcset' and 'sizes' if not already present. |
|
1049 if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) { |
|
1050 $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true ); |
|
1051 $size_array = array( $width, $height ); |
|
1052 |
|
1053 if ( is_array( $image_meta ) ) { |
|
1054 $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id ); |
|
1055 $sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id ); |
|
1056 |
|
1057 if ( $srcset && $sizes ) { |
|
1058 $attr['srcset'] = $srcset; |
|
1059 $attr['sizes'] = $sizes; |
|
1060 } |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 $attr = array_map( 'esc_attr', $attr ); |
|
1065 $html = '<img'; |
|
1066 |
|
1067 foreach ( $attr as $name => $value ) { |
|
1068 $html .= ' ' . $name . '="' . $value . '"'; |
|
1069 } |
|
1070 |
|
1071 $html .= ' />'; |
|
1072 |
|
1073 /** |
|
1074 * Filters the markup of header images. |
|
1075 * |
|
1076 * @since 4.4.0 |
|
1077 * |
|
1078 * @param string $html The HTML image tag markup being filtered. |
|
1079 * @param object $header The custom header object returned by 'get_custom_header()'. |
|
1080 * @param array $attr Array of the attributes for the image tag. |
|
1081 */ |
|
1082 return apply_filters( 'get_header_image_tag', $html, $header, $attr ); |
|
1083 } |
|
1084 |
|
1085 /** |
|
1086 * Display the image markup for a custom header image. |
|
1087 * |
|
1088 * @since 4.4.0 |
|
1089 * |
|
1090 * @param array $attr Optional. Attributes for the image markup. Default empty. |
|
1091 */ |
|
1092 function the_header_image_tag( $attr = array() ) { |
|
1093 echo get_header_image_tag( $attr ); |
|
1094 } |
|
1095 |
|
1096 /** |
1070 * Get random header image data from registered images in theme. |
1097 * Get random header image data from registered images in theme. |
1071 * |
1098 * |
1072 * @since 3.4.0 |
1099 * @since 3.4.0 |
1073 * |
1100 * |
1074 * @access private |
1101 * @access private |
1075 * |
1102 * |
1076 * @return string Path to header image |
1103 * @global array $_wp_default_headers |
1077 */ |
1104 * @staticvar object $_wp_random_header |
1078 |
1105 * |
|
1106 * @return object |
|
1107 */ |
1079 function _get_random_header_data() { |
1108 function _get_random_header_data() { |
1080 static $_wp_random_header; |
1109 static $_wp_random_header = null; |
1081 |
1110 |
1082 if ( empty( $_wp_random_header ) ) { |
1111 if ( empty( $_wp_random_header ) ) { |
1083 global $_wp_default_headers; |
1112 global $_wp_default_headers; |
1084 $header_image_mod = get_theme_mod( 'header_image', '' ); |
1113 $header_image_mod = get_theme_mod( 'header_image', '' ); |
1085 $headers = array(); |
1114 $headers = array(); |
1272 return false; |
1311 return false; |
1273 } |
1312 } |
1274 } |
1313 } |
1275 |
1314 |
1276 /** |
1315 /** |
|
1316 * Check whether a header video is set or not. |
|
1317 * |
|
1318 * @since 4.7.0 |
|
1319 * |
|
1320 * @see get_header_video_url() |
|
1321 * |
|
1322 * @return bool Whether a header video is set or not. |
|
1323 */ |
|
1324 function has_header_video() { |
|
1325 return (bool) get_header_video_url(); |
|
1326 } |
|
1327 |
|
1328 /** |
|
1329 * Retrieve header video URL for custom header. |
|
1330 * |
|
1331 * Uses a local video if present, or falls back to an external video. |
|
1332 * |
|
1333 * @since 4.7.0 |
|
1334 * |
|
1335 * @return string|false Header video URL or false if there is no video. |
|
1336 */ |
|
1337 function get_header_video_url() { |
|
1338 $id = absint( get_theme_mod( 'header_video' ) ); |
|
1339 $url = esc_url( get_theme_mod( 'external_header_video' ) ); |
|
1340 |
|
1341 if ( $id ) { |
|
1342 // Get the file URL from the attachment ID. |
|
1343 $url = wp_get_attachment_url( $id ); |
|
1344 } |
|
1345 |
|
1346 /** |
|
1347 * Filters the header video URL. |
|
1348 * |
|
1349 * @since 4.7.3 |
|
1350 * |
|
1351 * @param string $url Header video URL, if available. |
|
1352 */ |
|
1353 $url = apply_filters( 'get_header_video_url', $url ); |
|
1354 |
|
1355 if ( ! $id && ! $url ) { |
|
1356 return false; |
|
1357 } |
|
1358 |
|
1359 return esc_url_raw( set_url_scheme( $url ) ); |
|
1360 } |
|
1361 |
|
1362 /** |
|
1363 * Display header video URL. |
|
1364 * |
|
1365 * @since 4.7.0 |
|
1366 */ |
|
1367 function the_header_video_url() { |
|
1368 $video = get_header_video_url(); |
|
1369 if ( $video ) { |
|
1370 echo esc_url( $video ); |
|
1371 } |
|
1372 } |
|
1373 |
|
1374 /** |
|
1375 * Retrieve header video settings. |
|
1376 * |
|
1377 * @since 4.7.0 |
|
1378 * |
|
1379 * @return array |
|
1380 */ |
|
1381 function get_header_video_settings() { |
|
1382 $header = get_custom_header(); |
|
1383 $video_url = get_header_video_url(); |
|
1384 $video_type = wp_check_filetype( $video_url, wp_get_mime_types() ); |
|
1385 |
|
1386 $settings = array( |
|
1387 'mimeType' => '', |
|
1388 'posterUrl' => get_header_image(), |
|
1389 'videoUrl' => $video_url, |
|
1390 'width' => absint( $header->width ), |
|
1391 'height' => absint( $header->height ), |
|
1392 'minWidth' => 900, |
|
1393 'minHeight' => 500, |
|
1394 'l10n' => array( |
|
1395 'pause' => __( 'Pause' ), |
|
1396 'play' => __( 'Play' ), |
|
1397 'pauseSpeak' => __( 'Video is paused.'), |
|
1398 'playSpeak' => __( 'Video is playing.'), |
|
1399 ), |
|
1400 ); |
|
1401 |
|
1402 if ( preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video_url ) ) { |
|
1403 $settings['mimeType'] = 'video/x-youtube'; |
|
1404 } elseif ( ! empty( $video_type['type'] ) ) { |
|
1405 $settings['mimeType'] = $video_type['type']; |
|
1406 } |
|
1407 |
|
1408 return apply_filters( 'header_video_settings', $settings ); |
|
1409 } |
|
1410 |
|
1411 /** |
|
1412 * Check whether a custom header is set or not. |
|
1413 * |
|
1414 * @since 4.7.0 |
|
1415 * |
|
1416 * @return bool True if a custom header is set. False if not. |
|
1417 */ |
|
1418 function has_custom_header() { |
|
1419 if ( has_header_image() || ( has_header_video() && is_header_video_active() ) ) { |
|
1420 return true; |
|
1421 } |
|
1422 |
|
1423 return false; |
|
1424 } |
|
1425 |
|
1426 /** |
|
1427 * Checks whether the custom header video is eligible to show on the current page. |
|
1428 * |
|
1429 * @since 4.7.0 |
|
1430 * |
|
1431 * @return bool True if the custom header video should be shown. False if not. |
|
1432 */ |
|
1433 function is_header_video_active() { |
|
1434 if ( ! get_theme_support( 'custom-header', 'video' ) ) { |
|
1435 return false; |
|
1436 } |
|
1437 |
|
1438 $video_active_cb = get_theme_support( 'custom-header', 'video-active-callback' ); |
|
1439 |
|
1440 if ( empty( $video_active_cb ) || ! is_callable( $video_active_cb ) ) { |
|
1441 $show_video = true; |
|
1442 } else { |
|
1443 $show_video = call_user_func( $video_active_cb ); |
|
1444 } |
|
1445 |
|
1446 /** |
|
1447 * Modify whether the custom header video is eligible to show on the current page. |
|
1448 * |
|
1449 * @since 4.7.0 |
|
1450 * |
|
1451 * @param bool $show_video Whether the custom header video should be shown. Returns the value |
|
1452 * of the theme setting for the `custom-header`'s `video-active-callback`. |
|
1453 * If no callback is set, the default value is that of `is_front_page()`. |
|
1454 */ |
|
1455 return apply_filters( 'is_header_video_active', $show_video ); |
|
1456 } |
|
1457 |
|
1458 /** |
|
1459 * Retrieve the markup for a custom header. |
|
1460 * |
|
1461 * The container div will always be returned in the Customizer preview. |
|
1462 * |
|
1463 * @since 4.7.0 |
|
1464 * |
|
1465 * @return string The markup for a custom header on success. |
|
1466 */ |
|
1467 function get_custom_header_markup() { |
|
1468 if ( ! has_custom_header() && ! is_customize_preview() ) { |
|
1469 return ''; |
|
1470 } |
|
1471 |
|
1472 return sprintf( |
|
1473 '<div id="wp-custom-header" class="wp-custom-header">%s</div>', |
|
1474 get_header_image_tag() |
|
1475 ); |
|
1476 } |
|
1477 |
|
1478 /** |
|
1479 * Print the markup for a custom header. |
|
1480 * |
|
1481 * A container div will always be printed in the Customizer preview. |
|
1482 * |
|
1483 * @since 4.7.0 |
|
1484 */ |
|
1485 function the_custom_header_markup() { |
|
1486 $custom_header = get_custom_header_markup(); |
|
1487 if ( empty( $custom_header ) ) { |
|
1488 return; |
|
1489 } |
|
1490 |
|
1491 echo $custom_header; |
|
1492 |
|
1493 if ( is_header_video_active() && ( has_header_video() || is_customize_preview() ) ) { |
|
1494 wp_enqueue_script( 'wp-custom-header' ); |
|
1495 wp_localize_script( 'wp-custom-header', '_wpCustomHeaderSettings', get_header_video_settings() ); |
|
1496 } |
|
1497 } |
|
1498 |
|
1499 /** |
1277 * Retrieve background image for custom background. |
1500 * Retrieve background image for custom background. |
1278 * |
1501 * |
1279 * @since 3.0.0 |
1502 * @since 3.0.0 |
1280 * |
1503 * |
1281 * @return string |
1504 * @return string |
1329 |
1551 |
1330 if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) { |
1552 if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) { |
1331 $color = false; |
1553 $color = false; |
1332 } |
1554 } |
1333 |
1555 |
1334 if ( ! $background && ! $color ) |
1556 if ( ! $background && ! $color ) { |
|
1557 if ( is_customize_preview() ) { |
|
1558 echo '<style type="text/css" id="custom-background-css"></style>'; |
|
1559 } |
1335 return; |
1560 return; |
|
1561 } |
1336 |
1562 |
1337 $style = $color ? "background-color: #$color;" : ''; |
1563 $style = $color ? "background-color: #$color;" : ''; |
1338 |
1564 |
1339 if ( $background ) { |
1565 if ( $background ) { |
1340 $image = " background-image: url('$background');"; |
1566 $image = ' background-image: url("' . esc_url_raw( $background ) . '");'; |
1341 |
1567 |
|
1568 // Background Position. |
|
1569 $position_x = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) ); |
|
1570 $position_y = get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) ); |
|
1571 |
|
1572 if ( ! in_array( $position_x, array( 'left', 'center', 'right' ), true ) ) { |
|
1573 $position_x = 'left'; |
|
1574 } |
|
1575 |
|
1576 if ( ! in_array( $position_y, array( 'top', 'center', 'bottom' ), true ) ) { |
|
1577 $position_y = 'top'; |
|
1578 } |
|
1579 |
|
1580 $position = " background-position: $position_x $position_y;"; |
|
1581 |
|
1582 // Background Size. |
|
1583 $size = get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) ); |
|
1584 |
|
1585 if ( ! in_array( $size, array( 'auto', 'contain', 'cover' ), true ) ) { |
|
1586 $size = 'auto'; |
|
1587 } |
|
1588 |
|
1589 $size = " background-size: $size;"; |
|
1590 |
|
1591 // Background Repeat. |
1342 $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) ); |
1592 $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) ); |
1343 if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) ) |
1593 |
|
1594 if ( ! in_array( $repeat, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) { |
1344 $repeat = 'repeat'; |
1595 $repeat = 'repeat'; |
|
1596 } |
|
1597 |
1345 $repeat = " background-repeat: $repeat;"; |
1598 $repeat = " background-repeat: $repeat;"; |
1346 |
1599 |
1347 $position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) ); |
1600 // Background Scroll. |
1348 if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) ) |
|
1349 $position = 'left'; |
|
1350 $position = " background-position: top $position;"; |
|
1351 |
|
1352 $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) ); |
1601 $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) ); |
1353 if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) ) |
1602 |
|
1603 if ( 'fixed' !== $attachment ) { |
1354 $attachment = 'scroll'; |
1604 $attachment = 'scroll'; |
|
1605 } |
|
1606 |
1355 $attachment = " background-attachment: $attachment;"; |
1607 $attachment = " background-attachment: $attachment;"; |
1356 |
1608 |
1357 $style .= $image . $repeat . $position . $attachment; |
1609 $style .= $image . $position . $size . $repeat . $attachment; |
1358 } |
1610 } |
1359 ?> |
1611 ?> |
1360 <style type="text/css" id="custom-background-css"> |
1612 <style type="text/css" id="custom-background-css"> |
1361 body.custom-background { <?php echo trim( $style ); ?> } |
1613 body.custom-background { <?php echo trim( $style ); ?> } |
1362 </style> |
1614 </style> |
1363 <?php |
1615 <?php |
1364 } |
1616 } |
1365 |
1617 |
1366 /** |
1618 /** |
|
1619 * Render the Custom CSS style element. |
|
1620 * |
|
1621 * @since 4.7.0 |
|
1622 */ |
|
1623 function wp_custom_css_cb() { |
|
1624 $styles = wp_get_custom_css(); |
|
1625 if ( $styles || is_customize_preview() ) : ?> |
|
1626 <style type="text/css" id="wp-custom-css"> |
|
1627 <?php echo strip_tags( $styles ); // Note that esc_html() cannot be used because `div > span` is not interpreted properly. ?> |
|
1628 </style> |
|
1629 <?php endif; |
|
1630 } |
|
1631 |
|
1632 /** |
|
1633 * Fetch the `custom_css` post for a given theme. |
|
1634 * |
|
1635 * @since 4.7.0 |
|
1636 * |
|
1637 * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme. |
|
1638 * @return WP_Post|null The custom_css post or null if none exists. |
|
1639 */ |
|
1640 function wp_get_custom_css_post( $stylesheet = '' ) { |
|
1641 if ( empty( $stylesheet ) ) { |
|
1642 $stylesheet = get_stylesheet(); |
|
1643 } |
|
1644 |
|
1645 $custom_css_query_vars = array( |
|
1646 'post_type' => 'custom_css', |
|
1647 'post_status' => get_post_stati(), |
|
1648 'name' => sanitize_title( $stylesheet ), |
|
1649 'posts_per_page' => 1, |
|
1650 'no_found_rows' => true, |
|
1651 'cache_results' => true, |
|
1652 'update_post_meta_cache' => false, |
|
1653 'update_post_term_cache' => false, |
|
1654 'lazy_load_term_meta' => false, |
|
1655 ); |
|
1656 |
|
1657 $post = null; |
|
1658 if ( get_stylesheet() === $stylesheet ) { |
|
1659 $post_id = get_theme_mod( 'custom_css_post_id' ); |
|
1660 |
|
1661 if ( $post_id > 0 && get_post( $post_id ) ) { |
|
1662 $post = get_post( $post_id ); |
|
1663 } |
|
1664 |
|
1665 // `-1` indicates no post exists; no query necessary. |
|
1666 if ( ! $post && -1 !== $post_id ) { |
|
1667 $query = new WP_Query( $custom_css_query_vars ); |
|
1668 $post = $query->post; |
|
1669 /* |
|
1670 * Cache the lookup. See wp_update_custom_css_post(). |
|
1671 * @todo This should get cleared if a custom_css post is added/removed. |
|
1672 */ |
|
1673 set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 ); |
|
1674 } |
|
1675 } else { |
|
1676 $query = new WP_Query( $custom_css_query_vars ); |
|
1677 $post = $query->post; |
|
1678 } |
|
1679 |
|
1680 return $post; |
|
1681 } |
|
1682 |
|
1683 /** |
|
1684 * Fetch the saved Custom CSS content for rendering. |
|
1685 * |
|
1686 * @since 4.7.0 |
|
1687 * |
|
1688 * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme. |
|
1689 * @return string The Custom CSS Post content. |
|
1690 */ |
|
1691 function wp_get_custom_css( $stylesheet = '' ) { |
|
1692 $css = ''; |
|
1693 |
|
1694 if ( empty( $stylesheet ) ) { |
|
1695 $stylesheet = get_stylesheet(); |
|
1696 } |
|
1697 |
|
1698 $post = wp_get_custom_css_post( $stylesheet ); |
|
1699 if ( $post ) { |
|
1700 $css = $post->post_content; |
|
1701 } |
|
1702 |
|
1703 /** |
|
1704 * Filters the Custom CSS Output into the <head>. |
|
1705 * |
|
1706 * @since 4.7.0 |
|
1707 * |
|
1708 * @param string $css CSS pulled in from the Custom CSS CPT. |
|
1709 * @param string $stylesheet The theme stylesheet name. |
|
1710 */ |
|
1711 $css = apply_filters( 'wp_get_custom_css', $css, $stylesheet ); |
|
1712 |
|
1713 return $css; |
|
1714 } |
|
1715 |
|
1716 /** |
|
1717 * Update the `custom_css` post for a given theme. |
|
1718 * |
|
1719 * Inserts a `custom_css` post when one doesn't yet exist. |
|
1720 * |
|
1721 * @since 4.7.0 |
|
1722 * |
|
1723 * @param string $css CSS, stored in `post_content`. |
|
1724 * @param array $args { |
|
1725 * Args. |
|
1726 * |
|
1727 * @type string $preprocessed Pre-processed CSS, stored in `post_content_filtered`. Normally empty string. Optional. |
|
1728 * @type string $stylesheet Stylesheet (child theme) to update. Optional, defaults to current theme/stylesheet. |
|
1729 * } |
|
1730 * @return WP_Post|WP_Error Post on success, error on failure. |
|
1731 */ |
|
1732 function wp_update_custom_css_post( $css, $args = array() ) { |
|
1733 $args = wp_parse_args( $args, array( |
|
1734 'preprocessed' => '', |
|
1735 'stylesheet' => get_stylesheet(), |
|
1736 ) ); |
|
1737 |
|
1738 $data = array( |
|
1739 'css' => $css, |
|
1740 'preprocessed' => $args['preprocessed'], |
|
1741 ); |
|
1742 |
|
1743 /** |
|
1744 * Filters the `css` (`post_content`) and `preprocessed` (`post_content_filtered`) args for a `custom_css` post being updated. |
|
1745 * |
|
1746 * This filter can be used by plugin that offer CSS pre-processors, to store the original |
|
1747 * pre-processed CSS in `post_content_filtered` and then store processed CSS in `post_content`. |
|
1748 * When used in this way, the `post_content_filtered` should be supplied as the setting value |
|
1749 * instead of `post_content` via a the `customize_value_custom_css` filter, for example: |
|
1750 * |
|
1751 * <code> |
|
1752 * add_filter( 'customize_value_custom_css', function( $value, $setting ) { |
|
1753 * $post = wp_get_custom_css_post( $setting->stylesheet ); |
|
1754 * if ( $post && ! empty( $post->post_content_filtered ) ) { |
|
1755 * $css = $post->post_content_filtered; |
|
1756 * } |
|
1757 * return $css; |
|
1758 * }, 10, 2 ); |
|
1759 * </code> |
|
1760 * |
|
1761 * @since 4.7.0 |
|
1762 * @param array $data { |
|
1763 * Custom CSS data. |
|
1764 * |
|
1765 * @type string $css CSS stored in `post_content`. |
|
1766 * @type string $preprocessed Pre-processed CSS stored in `post_content_filtered`. Normally empty string. |
|
1767 * } |
|
1768 * @param array $args { |
|
1769 * The args passed into `wp_update_custom_css_post()` merged with defaults. |
|
1770 * |
|
1771 * @type string $css The original CSS passed in to be updated. |
|
1772 * @type string $preprocessed The original preprocessed CSS passed in to be updated. |
|
1773 * @type string $stylesheet The stylesheet (theme) being updated. |
|
1774 * } |
|
1775 */ |
|
1776 $data = apply_filters( 'update_custom_css_data', $data, array_merge( $args, compact( 'css' ) ) ); |
|
1777 |
|
1778 $post_data = array( |
|
1779 'post_title' => $args['stylesheet'], |
|
1780 'post_name' => sanitize_title( $args['stylesheet'] ), |
|
1781 'post_type' => 'custom_css', |
|
1782 'post_status' => 'publish', |
|
1783 'post_content' => $data['css'], |
|
1784 'post_content_filtered' => $data['preprocessed'], |
|
1785 ); |
|
1786 |
|
1787 // Update post if it already exists, otherwise create a new one. |
|
1788 $post = wp_get_custom_css_post( $args['stylesheet'] ); |
|
1789 if ( $post ) { |
|
1790 $post_data['ID'] = $post->ID; |
|
1791 $r = wp_update_post( wp_slash( $post_data ), true ); |
|
1792 } else { |
|
1793 $r = wp_insert_post( wp_slash( $post_data ), true ); |
|
1794 |
|
1795 if ( ! is_wp_error( $r ) ) { |
|
1796 if ( get_stylesheet() === $args['stylesheet'] ) { |
|
1797 set_theme_mod( 'custom_css_post_id', $r ); |
|
1798 } |
|
1799 |
|
1800 // Trigger creation of a revision. This should be removed once #30854 is resolved. |
|
1801 if ( 0 === count( wp_get_post_revisions( $r ) ) ) { |
|
1802 wp_save_post_revision( $r ); |
|
1803 } |
|
1804 } |
|
1805 } |
|
1806 |
|
1807 if ( is_wp_error( $r ) ) { |
|
1808 return $r; |
|
1809 } |
|
1810 return get_post( $r ); |
|
1811 } |
|
1812 |
|
1813 /** |
1367 * Add callback for custom TinyMCE editor stylesheets. |
1814 * Add callback for custom TinyMCE editor stylesheets. |
1368 * |
1815 * |
1369 * The parameter $stylesheet is the name of the stylesheet, relative to |
1816 * The parameter $stylesheet is the name of the stylesheet, relative to |
1370 * the theme root. It also accepts an array of stylesheets. |
1817 * the theme root. It also accepts an array of stylesheets. |
1371 * It is optional and defaults to 'editor-style.css'. |
1818 * It is optional and defaults to 'editor-style.css'. |
1460 if ( $file && file_exists( "$style_dir/$file" ) ) { |
1910 if ( $file && file_exists( "$style_dir/$file" ) ) { |
1461 $stylesheets[] = "$style_uri/$file"; |
1911 $stylesheets[] = "$style_uri/$file"; |
1462 } |
1912 } |
1463 } |
1913 } |
1464 } |
1914 } |
1465 return $stylesheets; |
1915 |
1466 } |
1916 /** |
1467 |
1917 * Filters the array of stylesheets applied to the editor. |
1468 /** |
1918 * |
1469 * Allows a theme to register its support of a certain feature |
1919 * @since 4.3.0 |
|
1920 * |
|
1921 * @param array $stylesheets Array of stylesheets to be applied to the editor. |
|
1922 */ |
|
1923 return apply_filters( 'editor_stylesheets', $stylesheets ); |
|
1924 } |
|
1925 |
|
1926 /** |
|
1927 * Expand a theme's starter content configuration using core-provided data. |
|
1928 * |
|
1929 * @since 4.7.0 |
|
1930 * |
|
1931 * @return array Array of starter content. |
|
1932 */ |
|
1933 function get_theme_starter_content() { |
|
1934 $theme_support = get_theme_support( 'starter-content' ); |
|
1935 if ( is_array( $theme_support ) && ! empty( $theme_support[0] ) && is_array( $theme_support[0] ) ) { |
|
1936 $config = $theme_support[0]; |
|
1937 } else { |
|
1938 $config = array(); |
|
1939 } |
|
1940 |
|
1941 $core_content = array( |
|
1942 'widgets' => array( |
|
1943 'text_business_info' => array( 'text', array( |
|
1944 'title' => _x( 'Find Us', 'Theme starter content' ), |
|
1945 'text' => join( '', array( |
|
1946 '<strong>' . _x( 'Address', 'Theme starter content' ) . "</strong>\n", |
|
1947 _x( '123 Main Street', 'Theme starter content' ) . "\n" . _x( 'New York, NY 10001', 'Theme starter content' ) . "\n\n", |
|
1948 '<strong>' . _x( 'Hours', 'Theme starter content' ) . "</strong>\n", |
|
1949 _x( 'Monday—Friday: 9:00AM–5:00PM', 'Theme starter content' ) . "\n" . _x( 'Saturday & Sunday: 11:00AM–3:00PM', 'Theme starter content' ) |
|
1950 ) ), |
|
1951 'filter' => true, |
|
1952 'visual' => true, |
|
1953 ) ), |
|
1954 'text_about' => array( 'text', array( |
|
1955 'title' => _x( 'About This Site', 'Theme starter content' ), |
|
1956 'text' => _x( 'This may be a good place to introduce yourself and your site or include some credits.', 'Theme starter content' ), |
|
1957 'filter' => true, |
|
1958 'visual' => true, |
|
1959 ) ), |
|
1960 'archives' => array( 'archives', array( |
|
1961 'title' => _x( 'Archives', 'Theme starter content' ), |
|
1962 ) ), |
|
1963 'calendar' => array( 'calendar', array( |
|
1964 'title' => _x( 'Calendar', 'Theme starter content' ), |
|
1965 ) ), |
|
1966 'categories' => array( 'categories', array( |
|
1967 'title' => _x( 'Categories', 'Theme starter content' ), |
|
1968 ) ), |
|
1969 'meta' => array( 'meta', array( |
|
1970 'title' => _x( 'Meta', 'Theme starter content' ), |
|
1971 ) ), |
|
1972 'recent-comments' => array( 'recent-comments', array( |
|
1973 'title' => _x( 'Recent Comments', 'Theme starter content' ), |
|
1974 ) ), |
|
1975 'recent-posts' => array( 'recent-posts', array( |
|
1976 'title' => _x( 'Recent Posts', 'Theme starter content' ), |
|
1977 ) ), |
|
1978 'search' => array( 'search', array( |
|
1979 'title' => _x( 'Search', 'Theme starter content' ), |
|
1980 ) ), |
|
1981 ), |
|
1982 'nav_menus' => array( |
|
1983 'link_home' => array( |
|
1984 'type' => 'custom', |
|
1985 'title' => _x( 'Home', 'Theme starter content' ), |
|
1986 'url' => home_url( '/' ), |
|
1987 ), |
|
1988 'page_home' => array( // Deprecated in favor of link_home. |
|
1989 'type' => 'post_type', |
|
1990 'object' => 'page', |
|
1991 'object_id' => '{{home}}', |
|
1992 ), |
|
1993 'page_about' => array( |
|
1994 'type' => 'post_type', |
|
1995 'object' => 'page', |
|
1996 'object_id' => '{{about}}', |
|
1997 ), |
|
1998 'page_blog' => array( |
|
1999 'type' => 'post_type', |
|
2000 'object' => 'page', |
|
2001 'object_id' => '{{blog}}', |
|
2002 ), |
|
2003 'page_news' => array( |
|
2004 'type' => 'post_type', |
|
2005 'object' => 'page', |
|
2006 'object_id' => '{{news}}', |
|
2007 ), |
|
2008 'page_contact' => array( |
|
2009 'type' => 'post_type', |
|
2010 'object' => 'page', |
|
2011 'object_id' => '{{contact}}', |
|
2012 ), |
|
2013 |
|
2014 'link_email' => array( |
|
2015 'title' => _x( 'Email', 'Theme starter content' ), |
|
2016 'url' => 'mailto:wordpress@example.com', |
|
2017 ), |
|
2018 'link_facebook' => array( |
|
2019 'title' => _x( 'Facebook', 'Theme starter content' ), |
|
2020 'url' => 'https://www.facebook.com/wordpress', |
|
2021 ), |
|
2022 'link_foursquare' => array( |
|
2023 'title' => _x( 'Foursquare', 'Theme starter content' ), |
|
2024 'url' => 'https://foursquare.com/', |
|
2025 ), |
|
2026 'link_github' => array( |
|
2027 'title' => _x( 'GitHub', 'Theme starter content' ), |
|
2028 'url' => 'https://github.com/wordpress/', |
|
2029 ), |
|
2030 'link_instagram' => array( |
|
2031 'title' => _x( 'Instagram', 'Theme starter content' ), |
|
2032 'url' => 'https://www.instagram.com/explore/tags/wordcamp/', |
|
2033 ), |
|
2034 'link_linkedin' => array( |
|
2035 'title' => _x( 'LinkedIn', 'Theme starter content' ), |
|
2036 'url' => 'https://www.linkedin.com/company/1089783', |
|
2037 ), |
|
2038 'link_pinterest' => array( |
|
2039 'title' => _x( 'Pinterest', 'Theme starter content' ), |
|
2040 'url' => 'https://www.pinterest.com/', |
|
2041 ), |
|
2042 'link_twitter' => array( |
|
2043 'title' => _x( 'Twitter', 'Theme starter content' ), |
|
2044 'url' => 'https://twitter.com/wordpress', |
|
2045 ), |
|
2046 'link_yelp' => array( |
|
2047 'title' => _x( 'Yelp', 'Theme starter content' ), |
|
2048 'url' => 'https://www.yelp.com', |
|
2049 ), |
|
2050 'link_youtube' => array( |
|
2051 'title' => _x( 'YouTube', 'Theme starter content' ), |
|
2052 'url' => 'https://www.youtube.com/channel/UCdof4Ju7amm1chz1gi1T2ZA', |
|
2053 ), |
|
2054 ), |
|
2055 'posts' => array( |
|
2056 'home' => array( |
|
2057 'post_type' => 'page', |
|
2058 'post_title' => _x( 'Home', 'Theme starter content' ), |
|
2059 'post_content' => _x( 'Welcome to your site! This is your homepage, which is what most visitors will see when they come to your site for the first time.', 'Theme starter content' ), |
|
2060 ), |
|
2061 'about' => array( |
|
2062 'post_type' => 'page', |
|
2063 'post_title' => _x( 'About', 'Theme starter content' ), |
|
2064 'post_content' => _x( 'You might be an artist who would like to introduce yourself and your work here or maybe you’re a business with a mission to describe.', 'Theme starter content' ), |
|
2065 ), |
|
2066 'contact' => array( |
|
2067 'post_type' => 'page', |
|
2068 'post_title' => _x( 'Contact', 'Theme starter content' ), |
|
2069 'post_content' => _x( 'This is a page with some basic contact information, such as an address and phone number. You might also try a plugin to add a contact form.', 'Theme starter content' ), |
|
2070 ), |
|
2071 'blog' => array( |
|
2072 'post_type' => 'page', |
|
2073 'post_title' => _x( 'Blog', 'Theme starter content' ), |
|
2074 ), |
|
2075 'news' => array( |
|
2076 'post_type' => 'page', |
|
2077 'post_title' => _x( 'News', 'Theme starter content' ), |
|
2078 ), |
|
2079 |
|
2080 'homepage-section' => array( |
|
2081 'post_type' => 'page', |
|
2082 'post_title' => _x( 'A homepage section', 'Theme starter content' ), |
|
2083 'post_content' => _x( 'This is an example of a homepage section. Homepage sections can be any page other than the homepage itself, including the page that shows your latest blog posts.', 'Theme starter content' ), |
|
2084 ), |
|
2085 ), |
|
2086 ); |
|
2087 |
|
2088 $content = array(); |
|
2089 |
|
2090 foreach ( $config as $type => $args ) { |
|
2091 switch( $type ) { |
|
2092 // Use options and theme_mods as-is. |
|
2093 case 'options' : |
|
2094 case 'theme_mods' : |
|
2095 $content[ $type ] = $config[ $type ]; |
|
2096 break; |
|
2097 |
|
2098 // Widgets are grouped into sidebars. |
|
2099 case 'widgets' : |
|
2100 foreach ( $config[ $type ] as $sidebar_id => $widgets ) { |
|
2101 foreach ( $widgets as $id => $widget ) { |
|
2102 if ( is_array( $widget ) ) { |
|
2103 |
|
2104 // Item extends core content. |
|
2105 if ( ! empty( $core_content[ $type ][ $id ] ) ) { |
|
2106 $widget = array( |
|
2107 $core_content[ $type ][ $id ][0], |
|
2108 array_merge( $core_content[ $type ][ $id ][1], $widget ), |
|
2109 ); |
|
2110 } |
|
2111 |
|
2112 $content[ $type ][ $sidebar_id ][] = $widget; |
|
2113 } elseif ( is_string( $widget ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $widget ] ) ) { |
|
2114 $content[ $type ][ $sidebar_id ][] = $core_content[ $type ][ $widget ]; |
|
2115 } |
|
2116 } |
|
2117 } |
|
2118 break; |
|
2119 |
|
2120 // And nav menu items are grouped into nav menus. |
|
2121 case 'nav_menus' : |
|
2122 foreach ( $config[ $type ] as $nav_menu_location => $nav_menu ) { |
|
2123 |
|
2124 // Ensure nav menus get a name. |
|
2125 if ( empty( $nav_menu['name'] ) ) { |
|
2126 $nav_menu['name'] = $nav_menu_location; |
|
2127 } |
|
2128 |
|
2129 $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name']; |
|
2130 |
|
2131 foreach ( $nav_menu['items'] as $id => $nav_menu_item ) { |
|
2132 if ( is_array( $nav_menu_item ) ) { |
|
2133 |
|
2134 // Item extends core content. |
|
2135 if ( ! empty( $core_content[ $type ][ $id ] ) ) { |
|
2136 $nav_menu_item = array_merge( $core_content[ $type ][ $id ], $nav_menu_item ); |
|
2137 } |
|
2138 |
|
2139 $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item; |
|
2140 } elseif ( is_string( $nav_menu_item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $nav_menu_item ] ) ) { |
|
2141 $content[ $type ][ $nav_menu_location ]['items'][] = $core_content[ $type ][ $nav_menu_item ]; |
|
2142 } |
|
2143 } |
|
2144 } |
|
2145 break; |
|
2146 |
|
2147 // Attachments are posts but have special treatment. |
|
2148 case 'attachments' : |
|
2149 foreach ( $config[ $type ] as $id => $item ) { |
|
2150 if ( ! empty( $item['file'] ) ) { |
|
2151 $content[ $type ][ $id ] = $item; |
|
2152 } |
|
2153 } |
|
2154 break; |
|
2155 |
|
2156 // All that's left now are posts (besides attachments). Not a default case for the sake of clarity and future work. |
|
2157 case 'posts' : |
|
2158 foreach ( $config[ $type ] as $id => $item ) { |
|
2159 if ( is_array( $item ) ) { |
|
2160 |
|
2161 // Item extends core content. |
|
2162 if ( ! empty( $core_content[ $type ][ $id ] ) ) { |
|
2163 $item = array_merge( $core_content[ $type ][ $id ], $item ); |
|
2164 } |
|
2165 |
|
2166 // Enforce a subset of fields. |
|
2167 $content[ $type ][ $id ] = wp_array_slice_assoc( |
|
2168 $item, |
|
2169 array( |
|
2170 'post_type', |
|
2171 'post_title', |
|
2172 'post_excerpt', |
|
2173 'post_name', |
|
2174 'post_content', |
|
2175 'menu_order', |
|
2176 'comment_status', |
|
2177 'thumbnail', |
|
2178 'template', |
|
2179 ) |
|
2180 ); |
|
2181 } elseif ( is_string( $item ) && ! empty( $core_content[ $type ][ $item ] ) ) { |
|
2182 $content[ $type ][ $item ] = $core_content[ $type ][ $item ]; |
|
2183 } |
|
2184 } |
|
2185 break; |
|
2186 } |
|
2187 } |
|
2188 |
|
2189 /** |
|
2190 * Filters the expanded array of starter content. |
|
2191 * |
|
2192 * @since 4.7.0 |
|
2193 * |
|
2194 * @param array $content Array of starter content. |
|
2195 * @param array $config Array of theme-specific starter content configuration. |
|
2196 */ |
|
2197 return apply_filters( 'get_theme_starter_content', $content, $config ); |
|
2198 } |
|
2199 |
|
2200 /** |
|
2201 * Registers theme support for a given feature. |
1470 * |
2202 * |
1471 * Must be called in the theme's functions.php file to work. |
2203 * Must be called in the theme's functions.php file to work. |
1472 * If attached to a hook, it must be after_setup_theme. |
2204 * If attached to a hook, it must be {@see 'after_setup_theme'}. |
1473 * The init hook may be too late for some features. |
2205 * The {@see 'init'} hook may be too late for some features. |
1474 * |
2206 * |
1475 * @since 2.9.0 |
2207 * @since 2.9.0 |
1476 * |
2208 * @since 3.6.0 The `html5` feature was added |
1477 * @param string $feature The feature being added. |
2209 * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption' |
|
2210 * @since 4.1.0 The `title-tag` feature was added |
|
2211 * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added |
|
2212 * @since 4.7.0 The `starter-content` feature was added |
|
2213 * |
|
2214 * @global array $_wp_theme_features |
|
2215 * |
|
2216 * @param string $feature The feature being added. Likely core values include 'post-formats', |
|
2217 * 'post-thumbnails', 'html5', 'custom-logo', 'custom-header-uploads', |
|
2218 * 'custom-header', 'custom-background', 'title-tag', 'starter-content', etc. |
|
2219 * @param mixed $args,... Optional extra arguments to pass along with certain features. |
1478 * @return void|bool False on failure, void otherwise. |
2220 * @return void|bool False on failure, void otherwise. |
1479 */ |
2221 */ |
1480 function add_theme_support( $feature ) { |
2222 function add_theme_support( $feature ) { |
1481 global $_wp_theme_features; |
2223 global $_wp_theme_features; |
1482 |
2224 |
1866 |
2682 |
1867 /** |
2683 /** |
1868 * Checks an attachment being deleted to see if it's a header or background image. |
2684 * Checks an attachment being deleted to see if it's a header or background image. |
1869 * |
2685 * |
1870 * If true it removes the theme modification which would be pointing at the deleted |
2686 * If true it removes the theme modification which would be pointing at the deleted |
1871 * attachment |
2687 * attachment. |
1872 * |
2688 * |
1873 * @access private |
2689 * @access private |
1874 * @since 3.0.0 |
2690 * @since 3.0.0 |
1875 * @param int $id the attachment id |
2691 * @since 4.3.0 Also removes `header_image_data`. |
|
2692 * @since 4.5.0 Also removes custom logo theme mods. |
|
2693 * |
|
2694 * @param int $id The attachment id. |
1876 */ |
2695 */ |
1877 function _delete_attachment_theme_mod( $id ) { |
2696 function _delete_attachment_theme_mod( $id ) { |
1878 $attachment_image = wp_get_attachment_url( $id ); |
2697 $attachment_image = wp_get_attachment_url( $id ); |
1879 $header_image = get_header_image(); |
2698 $header_image = get_header_image(); |
1880 $background_image = get_background_image(); |
2699 $background_image = get_background_image(); |
1881 |
2700 $custom_logo_id = get_theme_mod( 'custom_logo' ); |
1882 if ( $header_image && $header_image == $attachment_image ) |
2701 |
|
2702 if ( $custom_logo_id && $custom_logo_id == $id ) { |
|
2703 remove_theme_mod( 'custom_logo' ); |
|
2704 remove_theme_mod( 'header_text' ); |
|
2705 } |
|
2706 |
|
2707 if ( $header_image && $header_image == $attachment_image ) { |
1883 remove_theme_mod( 'header_image' ); |
2708 remove_theme_mod( 'header_image' ); |
1884 |
2709 remove_theme_mod( 'header_image_data' ); |
1885 if ( $background_image && $background_image == $attachment_image ) |
2710 } |
|
2711 |
|
2712 if ( $background_image && $background_image == $attachment_image ) { |
1886 remove_theme_mod( 'background_image' ); |
2713 remove_theme_mod( 'background_image' ); |
1887 } |
2714 } |
1888 |
2715 } |
1889 /** |
2716 |
1890 * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load |
2717 /** |
|
2718 * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load. |
|
2719 * |
|
2720 * See {@see 'after_switch_theme'}. |
1891 * |
2721 * |
1892 * @since 3.3.0 |
2722 * @since 3.3.0 |
1893 */ |
2723 */ |
1894 function check_theme_switched() { |
2724 function check_theme_switched() { |
1895 if ( $stylesheet = get_option( 'theme_switched' ) ) { |
2725 if ( $stylesheet = get_option( 'theme_switched' ) ) { |
1896 $old_theme = wp_get_theme( $stylesheet ); |
2726 $old_theme = wp_get_theme( $stylesheet ); |
1897 |
2727 |
1898 // Prevent retrieve_widgets() from running since Customizer already called it up front |
2728 // Prevent widget & menu mapping from running since Customizer already called it up front |
1899 if ( get_option( 'theme_switched_via_customizer' ) ) { |
2729 if ( get_option( 'theme_switched_via_customizer' ) ) { |
|
2730 remove_action( 'after_switch_theme', '_wp_menus_changed' ); |
1900 remove_action( 'after_switch_theme', '_wp_sidebars_changed' ); |
2731 remove_action( 'after_switch_theme', '_wp_sidebars_changed' ); |
1901 update_option( 'theme_switched_via_customizer', false ); |
2732 update_option( 'theme_switched_via_customizer', false ); |
1902 } |
2733 } |
1903 |
2734 |
1904 if ( $old_theme->exists() ) { |
2735 if ( $old_theme->exists() ) { |
1916 * @param WP_Theme $old_theme WP_Theme instance of the old theme. |
2747 * @param WP_Theme $old_theme WP_Theme instance of the old theme. |
1917 */ |
2748 */ |
1918 do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme ); |
2749 do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme ); |
1919 } else { |
2750 } else { |
1920 /** This action is documented in wp-includes/theme.php */ |
2751 /** This action is documented in wp-includes/theme.php */ |
1921 do_action( 'after_switch_theme', $stylesheet ); |
2752 do_action( 'after_switch_theme', $stylesheet, $old_theme ); |
1922 } |
2753 } |
|
2754 flush_rewrite_rules(); |
1923 |
2755 |
1924 update_option( 'theme_switched', false ); |
2756 update_option( 'theme_switched', false ); |
1925 } |
2757 } |
1926 } |
2758 } |
1927 |
2759 |
1928 /** |
2760 /** |
1929 * Includes and instantiates the WP_Customize_Manager class. |
2761 * Includes and instantiates the WP_Customize_Manager class. |
1930 * |
2762 * |
1931 * Fires when ?wp_customize=on or on wp-admin/customize.php. |
2763 * Loads the Customizer at plugins_loaded when accessing the customize.php admin |
|
2764 * page or when any request includes a wp_customize=on param or a customize_changeset |
|
2765 * param (a UUID). This param is a signal for whether to bootstrap the Customizer when |
|
2766 * WordPress is loading, especially in the Customizer preview |
|
2767 * or when making Customizer Ajax requests for widgets or menus. |
1932 * |
2768 * |
1933 * @since 3.4.0 |
2769 * @since 3.4.0 |
|
2770 * |
|
2771 * @global WP_Customize_Manager $wp_customize |
1934 */ |
2772 */ |
1935 function _wp_customize_include() { |
2773 function _wp_customize_include() { |
1936 if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] ) |
2774 |
1937 || ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) ) |
2775 $is_customize_admin_page = ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) ); |
1938 ) ) |
2776 $should_include = ( |
|
2777 $is_customize_admin_page |
|
2778 || |
|
2779 ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] ) |
|
2780 || |
|
2781 ( ! empty( $_GET['customize_changeset_uuid'] ) || ! empty( $_POST['customize_changeset_uuid'] ) ) |
|
2782 ); |
|
2783 |
|
2784 if ( ! $should_include ) { |
1939 return; |
2785 return; |
1940 |
2786 } |
1941 require( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); |
2787 |
1942 // Init Customize class |
2788 /* |
1943 $GLOBALS['wp_customize'] = new WP_Customize_Manager; |
2789 * Note that wp_unslash() is not being used on the input vars because it is |
|
2790 * called before wp_magic_quotes() gets called. Besides this fact, none of |
|
2791 * the values should contain any characters needing slashes anyway. |
|
2792 */ |
|
2793 $keys = array( 'changeset_uuid', 'customize_changeset_uuid', 'customize_theme', 'theme', 'customize_messenger_channel', 'customize_autosaved' ); |
|
2794 $input_vars = array_merge( |
|
2795 wp_array_slice_assoc( $_GET, $keys ), |
|
2796 wp_array_slice_assoc( $_POST, $keys ) |
|
2797 ); |
|
2798 |
|
2799 $theme = null; |
|
2800 $changeset_uuid = false; // Value false indicates UUID should be determined after_setup_theme to either re-use existing saved changeset or else generate a new UUID if none exists. |
|
2801 $messenger_channel = null; |
|
2802 $autosaved = null; |
|
2803 $branching = false; // Set initially fo false since defaults to true for back-compat; can be overridden via the customize_changeset_branching filter. |
|
2804 |
|
2805 if ( $is_customize_admin_page && isset( $input_vars['changeset_uuid'] ) ) { |
|
2806 $changeset_uuid = sanitize_key( $input_vars['changeset_uuid'] ); |
|
2807 } elseif ( ! empty( $input_vars['customize_changeset_uuid'] ) ) { |
|
2808 $changeset_uuid = sanitize_key( $input_vars['customize_changeset_uuid'] ); |
|
2809 } |
|
2810 |
|
2811 // Note that theme will be sanitized via WP_Theme. |
|
2812 if ( $is_customize_admin_page && isset( $input_vars['theme'] ) ) { |
|
2813 $theme = $input_vars['theme']; |
|
2814 } elseif ( isset( $input_vars['customize_theme'] ) ) { |
|
2815 $theme = $input_vars['customize_theme']; |
|
2816 } |
|
2817 |
|
2818 if ( ! empty( $input_vars['customize_autosaved'] ) ) { |
|
2819 $autosaved = true; |
|
2820 } |
|
2821 |
|
2822 if ( isset( $input_vars['customize_messenger_channel'] ) ) { |
|
2823 $messenger_channel = sanitize_key( $input_vars['customize_messenger_channel'] ); |
|
2824 } |
|
2825 |
|
2826 /* |
|
2827 * Note that settings must be previewed even outside the customizer preview |
|
2828 * and also in the customizer pane itself. This is to enable loading an existing |
|
2829 * changeset into the customizer. Previewing the settings only has to be prevented |
|
2830 * here in the case of a customize_save action because this will cause WP to think |
|
2831 * there is nothing changed that needs to be saved. |
|
2832 */ |
|
2833 $is_customize_save_action = ( |
|
2834 wp_doing_ajax() |
|
2835 && |
|
2836 isset( $_REQUEST['action'] ) |
|
2837 && |
|
2838 'customize_save' === wp_unslash( $_REQUEST['action'] ) |
|
2839 ); |
|
2840 $settings_previewed = ! $is_customize_save_action; |
|
2841 |
|
2842 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; |
|
2843 $GLOBALS['wp_customize'] = new WP_Customize_Manager( compact( 'changeset_uuid', 'theme', 'messenger_channel', 'settings_previewed', 'autosaved', 'branching' ) ); |
|
2844 } |
|
2845 |
|
2846 /** |
|
2847 * Publishes a snapshot's changes. |
|
2848 * |
|
2849 * @since 4.7.0 |
|
2850 * @access private |
|
2851 * |
|
2852 * @global wpdb $wpdb WordPress database abstraction object. |
|
2853 * @global WP_Customize_Manager $wp_customize Customizer instance. |
|
2854 * |
|
2855 * @param string $new_status New post status. |
|
2856 * @param string $old_status Old post status. |
|
2857 * @param WP_Post $changeset_post Changeset post object. |
|
2858 */ |
|
2859 function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) { |
|
2860 global $wp_customize, $wpdb; |
|
2861 |
|
2862 $is_publishing_changeset = ( |
|
2863 'customize_changeset' === $changeset_post->post_type |
|
2864 && |
|
2865 'publish' === $new_status |
|
2866 && |
|
2867 'publish' !== $old_status |
|
2868 ); |
|
2869 if ( ! $is_publishing_changeset ) { |
|
2870 return; |
|
2871 } |
|
2872 |
|
2873 if ( empty( $wp_customize ) ) { |
|
2874 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; |
|
2875 $wp_customize = new WP_Customize_Manager( array( |
|
2876 'changeset_uuid' => $changeset_post->post_name, |
|
2877 'settings_previewed' => false, |
|
2878 ) ); |
|
2879 } |
|
2880 |
|
2881 if ( ! did_action( 'customize_register' ) ) { |
|
2882 /* |
|
2883 * When running from CLI or Cron, the customize_register action will need |
|
2884 * to be triggered in order for core, themes, and plugins to register their |
|
2885 * settings. Normally core will add_action( 'customize_register' ) at |
|
2886 * priority 10 to register the core settings, and if any themes/plugins |
|
2887 * also add_action( 'customize_register' ) at the same priority, they |
|
2888 * will have a $wp_customize with those settings registered since they |
|
2889 * call add_action() afterward, normally. However, when manually doing |
|
2890 * the customize_register action after the setup_theme, then the order |
|
2891 * will be reversed for two actions added at priority 10, resulting in |
|
2892 * the core settings no longer being available as expected to themes/plugins. |
|
2893 * So the following manually calls the method that registers the core |
|
2894 * settings up front before doing the action. |
|
2895 */ |
|
2896 remove_action( 'customize_register', array( $wp_customize, 'register_controls' ) ); |
|
2897 $wp_customize->register_controls(); |
|
2898 |
|
2899 /** This filter is documented in /wp-includes/class-wp-customize-manager.php */ |
|
2900 do_action( 'customize_register', $wp_customize ); |
|
2901 } |
|
2902 $wp_customize->_publish_changeset_values( $changeset_post->ID ) ; |
|
2903 |
|
2904 /* |
|
2905 * Trash the changeset post if revisions are not enabled. Unpublished |
|
2906 * changesets by default get garbage collected due to the auto-draft status. |
|
2907 * When a changeset post is published, however, it would no longer get cleaned |
|
2908 * out. Ths is a problem when the changeset posts are never displayed anywhere, |
|
2909 * since they would just be endlessly piling up. So here we use the revisions |
|
2910 * feature to indicate whether or not a published changeset should get trashed |
|
2911 * and thus garbage collected. |
|
2912 */ |
|
2913 if ( ! wp_revisions_enabled( $changeset_post ) ) { |
|
2914 $wp_customize->trash_changeset_post( $changeset_post->ID ); |
|
2915 } |
|
2916 } |
|
2917 |
|
2918 /** |
|
2919 * Filters changeset post data upon insert to ensure post_name is intact. |
|
2920 * |
|
2921 * This is needed to prevent the post_name from being dropped when the post is |
|
2922 * transitioned into pending status by a contributor. |
|
2923 * |
|
2924 * @since 4.7.0 |
|
2925 * @see wp_insert_post() |
|
2926 * |
|
2927 * @param array $post_data An array of slashed post data. |
|
2928 * @param array $supplied_post_data An array of sanitized, but otherwise unmodified post data. |
|
2929 * @returns array Filtered data. |
|
2930 */ |
|
2931 function _wp_customize_changeset_filter_insert_post_data( $post_data, $supplied_post_data ) { |
|
2932 if ( isset( $post_data['post_type'] ) && 'customize_changeset' === $post_data['post_type'] ) { |
|
2933 |
|
2934 // Prevent post_name from being dropped, such as when contributor saves a changeset post as pending. |
|
2935 if ( empty( $post_data['post_name'] ) && ! empty( $supplied_post_data['post_name'] ) ) { |
|
2936 $post_data['post_name'] = $supplied_post_data['post_name']; |
|
2937 } |
|
2938 } |
|
2939 return $post_data; |
1944 } |
2940 } |
1945 |
2941 |
1946 /** |
2942 /** |
1947 * Adds settings for the customize-loader script. |
2943 * Adds settings for the customize-loader script. |
1948 * |
2944 * |
1949 * @since 3.4.0 |
2945 * @since 3.4.0 |
1950 */ |
2946 */ |
1951 function _wp_customize_loader_settings() { |
2947 function _wp_customize_loader_settings() { |
1952 global $wp_scripts; |
|
1953 |
|
1954 $admin_origin = parse_url( admin_url() ); |
2948 $admin_origin = parse_url( admin_url() ); |
1955 $home_origin = parse_url( home_url() ); |
2949 $home_origin = parse_url( home_url() ); |
1956 $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) ); |
2950 $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) ); |
1957 |
2951 |
1958 $browser = array( |
2952 $browser = array( |
2044 function is_customize_preview() { |
3049 function is_customize_preview() { |
2045 global $wp_customize; |
3050 global $wp_customize; |
2046 |
3051 |
2047 return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview(); |
3052 return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview(); |
2048 } |
3053 } |
|
3054 |
|
3055 /** |
|
3056 * Make sure that auto-draft posts get their post_date bumped or status changed to draft to prevent premature garbage-collection. |
|
3057 * |
|
3058 * When a changeset is updated but remains an auto-draft, ensure the post_date |
|
3059 * for the auto-draft posts remains the same so that it will be |
|
3060 * garbage-collected at the same time by `wp_delete_auto_drafts()`. Otherwise, |
|
3061 * if the changeset is updated to be a draft then update the posts |
|
3062 * to have a far-future post_date so that they will never be garbage collected |
|
3063 * unless the changeset post itself is deleted. |
|
3064 * |
|
3065 * When a changeset is updated to be a persistent draft or to be scheduled for |
|
3066 * publishing, then transition any dependent auto-drafts to a draft status so |
|
3067 * that they likewise will not be garbage-collected but also so that they can |
|
3068 * be edited in the admin before publishing since there is not yet a post/page |
|
3069 * editing flow in the Customizer. See #39752. |
|
3070 * |
|
3071 * @link https://core.trac.wordpress.org/ticket/39752 |
|
3072 * |
|
3073 * @since 4.8.0 |
|
3074 * @access private |
|
3075 * @see wp_delete_auto_drafts() |
|
3076 * |
|
3077 * @param string $new_status Transition to this post status. |
|
3078 * @param string $old_status Previous post status. |
|
3079 * @param \WP_Post $post Post data. |
|
3080 * @global wpdb $wpdb |
|
3081 */ |
|
3082 function _wp_keep_alive_customize_changeset_dependent_auto_drafts( $new_status, $old_status, $post ) { |
|
3083 global $wpdb; |
|
3084 unset( $old_status ); |
|
3085 |
|
3086 // Short-circuit if not a changeset or if the changeset was published. |
|
3087 if ( 'customize_changeset' !== $post->post_type || 'publish' === $new_status ) { |
|
3088 return; |
|
3089 } |
|
3090 |
|
3091 $data = json_decode( $post->post_content, true ); |
|
3092 if ( empty( $data['nav_menus_created_posts']['value'] ) ) { |
|
3093 return; |
|
3094 } |
|
3095 |
|
3096 /* |
|
3097 * Actually, in lieu of keeping alive, trash any customization drafts here if the changeset itself is |
|
3098 * getting trashed. This is needed because when a changeset transitions to a draft, then any of the |
|
3099 * dependent auto-draft post/page stubs will also get transitioned to customization drafts which |
|
3100 * are then visible in the WP Admin. We cannot wait for the deletion of the changeset in which |
|
3101 * _wp_delete_customize_changeset_dependent_auto_drafts() will be called, since they need to be |
|
3102 * trashed to remove from visibility immediately. |
|
3103 */ |
|
3104 if ( 'trash' === $new_status ) { |
|
3105 foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) { |
|
3106 if ( ! empty( $post_id ) && 'draft' === get_post_status( $post_id ) ) { |
|
3107 wp_trash_post( $post_id ); |
|
3108 } |
|
3109 } |
|
3110 return; |
|
3111 } |
|
3112 |
|
3113 $post_args = array(); |
|
3114 if ( 'auto-draft' === $new_status ) { |
|
3115 /* |
|
3116 * Keep the post date for the post matching the changeset |
|
3117 * so that it will not be garbage-collected before the changeset. |
|
3118 */ |
|
3119 $post_args['post_date'] = $post->post_date; // Note wp_delete_auto_drafts() only looks at this date. |
|
3120 } else { |
|
3121 /* |
|
3122 * Since the changeset no longer has an auto-draft (and it is not published) |
|
3123 * it is now a persistent changeset, a long-lived draft, and so any |
|
3124 * associated auto-draft posts should likewise transition into having a draft |
|
3125 * status. These drafts will be treated differently than regular drafts in |
|
3126 * that they will be tied to the given changeset. The publish metabox is |
|
3127 * replaced with a notice about how the post is part of a set of customized changes |
|
3128 * which will be published when the changeset is published. |
|
3129 */ |
|
3130 $post_args['post_status'] = 'draft'; |
|
3131 } |
|
3132 |
|
3133 foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) { |
|
3134 if ( empty( $post_id ) || 'auto-draft' !== get_post_status( $post_id ) ) { |
|
3135 continue; |
|
3136 } |
|
3137 $wpdb->update( |
|
3138 $wpdb->posts, |
|
3139 $post_args, |
|
3140 array( 'ID' => $post_id ) |
|
3141 ); |
|
3142 clean_post_cache( $post_id ); |
|
3143 } |
|
3144 } |