Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
2.27% |
2 / 88 |
|
0.00% |
0 / 8 |
CRAP | n/a |
0 / 0 |
|
| wpcomsh_map_caps | |
40.00% |
2 / 5 |
|
0.00% |
0 / 1 |
7.46 | |||
| wpcomsh_remove_theme_delete_button | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
| wpcomsh_handle_atomic_premium_theme_option | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
| wpcomsh_jetpack_wpcom_theme_skip_download | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
56 | |||
| wpcomsh_jetpack_wpcom_theme_delete | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
110 | |||
| wpcomsh_add_wpcom_suffix_to_theme_endpoint_response | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
| wpcomsh_load_theme_compat_file | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
72 | |||
| wpcomsh_site_icon_url | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * Managed themes file. |
| 4 | * |
| 5 | * @package wpcomsh |
| 6 | */ |
| 7 | |
| 8 | /** |
| 9 | * Filters a user's capabilities depending on specific context and/or privilege. |
| 10 | * |
| 11 | * @param string[] $caps Primitive capabilities required of the user. |
| 12 | * @param string $cap Capability being checked. |
| 13 | * @return string[] |
| 14 | */ |
| 15 | function wpcomsh_map_caps( $caps, $cap ) { |
| 16 | if ( 'edit_themes' === $cap ) { |
| 17 | $theme = wp_get_theme(); |
| 18 | |
| 19 | if ( wpcomsh_is_wpcom_premium_theme( $theme->get_stylesheet() ) && 'Automattic' !== $theme->get( 'Author' ) ) { |
| 20 | $caps[] = 'do_not_allow'; |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | return $caps; |
| 25 | } |
| 26 | add_action( 'map_meta_cap', 'wpcomsh_map_caps', 10, 2 ); |
| 27 | |
| 28 | /** |
| 29 | * Removes theme delete button. |
| 30 | * |
| 31 | * @param array $prepared_themes Prepared themes. |
| 32 | * |
| 33 | * @return array |
| 34 | */ |
| 35 | function wpcomsh_remove_theme_delete_button( $prepared_themes ) { |
| 36 | foreach ( $prepared_themes as $theme_slug => $theme_data ) { |
| 37 | if ( wpcomsh_is_symlinked_storefront_theme( $theme_slug ) ) { |
| 38 | $prepared_themes[ $theme_slug ]['actions']['delete'] = ''; |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | return $prepared_themes; |
| 43 | } |
| 44 | add_filter( 'wp_prepare_themes_for_js', 'wpcomsh_remove_theme_delete_button' ); |
| 45 | |
| 46 | /** |
| 47 | * Returns the value for the `at_wpcom_premium_theme` option, which |
| 48 | * makes sure a stylesheet is returned only if the current theme has been |
| 49 | * symlinked and is a WP.com premium theme. |
| 50 | * |
| 51 | * @return string|bool The WP.com premium theme stylesheet or false if theme is not linked or a not premium theme. |
| 52 | */ |
| 53 | function wpcomsh_handle_atomic_premium_theme_option() { |
| 54 | $stylesheet = wp_get_theme()->get_stylesheet(); |
| 55 | |
| 56 | if ( wpcomsh_is_theme_symlinked( $stylesheet ) && wpcomsh_is_wpcom_premium_theme( $stylesheet ) ) { |
| 57 | return sprintf( 'premium/%s', $stylesheet ); |
| 58 | } |
| 59 | |
| 60 | return false; |
| 61 | } |
| 62 | add_filter( 'pre_option_at_wpcom_premium_theme', 'wpcomsh_handle_atomic_premium_theme_option' ); |
| 63 | |
| 64 | /** |
| 65 | * Symlinks WP.com themes instead of downloading them. |
| 66 | * |
| 67 | * @param bool $skip_download_filter_result Whether to skip the standard method of downloading and validating a WP.com theme. |
| 68 | * @param string $theme_slug Theme slug. If it is a WP.com theme it should be suffixed with `-wpcom`. |
| 69 | * |
| 70 | * @return bool|\WP_Error |
| 71 | */ |
| 72 | function wpcomsh_jetpack_wpcom_theme_skip_download( $skip_download_filter_result, $theme_slug ) { |
| 73 | $theme_type = wpcomsh_get_wpcom_theme_type( $theme_slug ); |
| 74 | |
| 75 | // If we are dealing with a non WPCom theme, don't interfere. |
| 76 | if ( ! $theme_type ) { |
| 77 | return false; |
| 78 | } |
| 79 | |
| 80 | if ( wpcomsh_is_theme_symlinked( $theme_slug ) ) { |
| 81 | error_log( "WPComSH: WP.com theme with slug: {$theme_slug} is already installed/symlinked." ); //phpcs:ignore |
| 82 | |
| 83 | return new WP_Error( 'wpcom_theme_already_installed', 'The WP.com theme is already installed/symlinked.' ); |
| 84 | } |
| 85 | |
| 86 | $was_theme_symlinked = wpcomsh_symlink_theme( $theme_slug, $theme_type ); |
| 87 | |
| 88 | if ( is_wp_error( $was_theme_symlinked ) ) { |
| 89 | return $was_theme_symlinked; |
| 90 | } |
| 91 | |
| 92 | wpcomsh_delete_theme_cache( $theme_slug ); |
| 93 | |
| 94 | // Skip the theme installation as we've "installed" (symlinked) it manually above. |
| 95 | add_filter( |
| 96 | 'jetpack_wpcom_theme_install', |
| 97 | function () use ( $was_theme_symlinked ) { |
| 98 | return $was_theme_symlinked; |
| 99 | }, |
| 100 | 10, |
| 101 | 2 |
| 102 | ); |
| 103 | |
| 104 | // If the installed WPCom theme is a child theme, we need to symlink its parent theme as well. |
| 105 | if ( |
| 106 | wpcomsh_is_wpcom_child_theme( $theme_slug ) && |
| 107 | ! wpcomsh_is_theme_symlinked( wp_get_theme( $theme_slug )->get_template() ) |
| 108 | ) { |
| 109 | $was_parent_theme_symlinked = wpcomsh_symlink_parent_theme( $theme_slug ); |
| 110 | |
| 111 | if ( ! $was_parent_theme_symlinked ) { |
| 112 | return new WP_Error( 'wpcom_theme_installation_failed', "Can't install specified WP.com theme. Check error log for more details." ); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | return true; |
| 117 | } |
| 118 | add_filter( 'jetpack_wpcom_theme_skip_download', 'wpcomsh_jetpack_wpcom_theme_skip_download', 10, 2 ); |
| 119 | |
| 120 | /** |
| 121 | * Un-symlinks WP.com themes instead of deleting them. |
| 122 | * |
| 123 | * @param bool $use_alternative_delete_method Whether to use the alternative method of deleting a WP.com theme. |
| 124 | * @param string $theme_slug Theme slug. If it is a WP.com theme it should be suffixed with `-wpcom`. |
| 125 | * |
| 126 | * @return bool |
| 127 | */ |
| 128 | function wpcomsh_jetpack_wpcom_theme_delete( $use_alternative_delete_method, $theme_slug ) { |
| 129 | if ( ! wpcomsh_is_wpcom_theme( $theme_slug ) || ! wpcomsh_is_theme_symlinked( $theme_slug ) ) { |
| 130 | return false; |
| 131 | } |
| 132 | |
| 133 | $theme = wp_get_theme( $theme_slug ); |
| 134 | if ( ! $theme->exists() ) { |
| 135 | return false; |
| 136 | } |
| 137 | |
| 138 | $num_child_themes = wpcomsh_count_child_themes( $theme->get_stylesheet() ); |
| 139 | if ( $num_child_themes > 0 ) { |
| 140 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions |
| 141 | error_log( 'WPComSH: Cannot remove parent theme. It still has installed child themes.' ); |
| 142 | return false; |
| 143 | } |
| 144 | |
| 145 | $is_child_theme = wpcomsh_is_wpcom_child_theme( $theme_slug ); |
| 146 | |
| 147 | /* |
| 148 | * Remember how many child themes there are before we delete this one. |
| 149 | * That way, we have the same count later, whether the previous themes list was cached or not. |
| 150 | */ |
| 151 | $num_themes_with_same_parent = 0; |
| 152 | if ( $is_child_theme ) { |
| 153 | $num_themes_with_same_parent = wpcomsh_count_child_themes( $theme->get_template() ); |
| 154 | } |
| 155 | |
| 156 | $was_theme_unsymlinked = wpcomsh_delete_symlinked_theme( $theme_slug ); |
| 157 | if ( is_wp_error( $was_theme_unsymlinked ) ) { |
| 158 | return $was_theme_unsymlinked; |
| 159 | } |
| 160 | |
| 161 | // If the theme was an only child, we need to unsymlink the parent theme as well. |
| 162 | if ( $is_child_theme && $num_themes_with_same_parent === 1 ) { |
| 163 | $parent_theme = $theme->get_template(); |
| 164 | if ( wpcomsh_is_wpcom_theme( $parent_theme ) ) { |
| 165 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions |
| 166 | error_log( 'WPComSH: Unsymlinking parent theme.' ); |
| 167 | |
| 168 | /* |
| 169 | * Ignore result because the child theme is already removed, |
| 170 | * and any problem should be logged by the deletion function. |
| 171 | */ |
| 172 | wpcomsh_delete_symlinked_theme( $parent_theme ); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | return true; |
| 177 | } |
| 178 | add_filter( 'jetpack_wpcom_theme_delete', 'wpcomsh_jetpack_wpcom_theme_delete', 10, 2 ); |
| 179 | |
| 180 | /** |
| 181 | * When a request is made to Jetpack Themes API, we need to distinguish between a WP.com theme |
| 182 | * and a WP.org theme in the response. This function adds/modifies the `theme_uri` field of a theme |
| 183 | * changing it to `https://wordpress.com/theme/{$theme_slug}` if a theme is a WP.com one. |
| 184 | * |
| 185 | * @param array $formatted_theme Array containing the Jetpack Themes API data to be sent to WP.com. |
| 186 | * |
| 187 | * @return array The original or modified theme info array. |
| 188 | */ |
| 189 | function wpcomsh_add_wpcom_suffix_to_theme_endpoint_response( $formatted_theme ) { |
| 190 | if ( empty( $formatted_theme['id'] ) ) { |
| 191 | return $formatted_theme; |
| 192 | } |
| 193 | |
| 194 | $theme_slug = $formatted_theme['id']; |
| 195 | $is_storefront = 'storefront' === $theme_slug; |
| 196 | |
| 197 | if ( wpcomsh_is_theme_symlinked( $theme_slug ) && ! $is_storefront ) { |
| 198 | $formatted_theme['theme_uri'] = "https://wordpress.com/theme/{$theme_slug}"; |
| 199 | } |
| 200 | |
| 201 | return $formatted_theme; |
| 202 | } |
| 203 | add_filter( 'jetpack_format_theme_details', 'wpcomsh_add_wpcom_suffix_to_theme_endpoint_response' ); |
| 204 | |
| 205 | /** |
| 206 | * Load a WordPress.com theme compat file, if it exists. |
| 207 | */ |
| 208 | function wpcomsh_load_theme_compat_file() { |
| 209 | if ( defined( 'WP_INSTALLING' ) && 'wp-activate.php' !== $GLOBALS['pagenow'] ) { |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | // Many wpcom.php files call $themecolors directly. Ease the pain. |
| 214 | global $themecolors; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- included files might need the global. |
| 215 | |
| 216 | $template_path = get_template_directory(); |
| 217 | $stylesheet_path = get_stylesheet_directory(); |
| 218 | $file = '/inc/wpcom.php'; |
| 219 | |
| 220 | // Look also in /includes as alternate location, since premium theme partners may use that convention. |
| 221 | if ( ! file_exists( $template_path . $file ) && ! file_exists( $stylesheet_path . $file ) ) { |
| 222 | $file = '/includes/wpcom.php'; |
| 223 | } |
| 224 | |
| 225 | // Include 'em. Child themes first, just like core. |
| 226 | if ( $template_path !== $stylesheet_path && file_exists( $stylesheet_path . $file ) ) { |
| 227 | include_once $stylesheet_path . $file; |
| 228 | } |
| 229 | |
| 230 | if ( file_exists( $template_path . $file ) ) { |
| 231 | include_once $template_path . $file; |
| 232 | } |
| 233 | } |
| 234 | add_action( 'after_setup_theme', 'wpcomsh_load_theme_compat_file', 0 ); // Hook early so that after_setup_theme can still be used at default priority. |
| 235 | |
| 236 | /** |
| 237 | * Provides a favicon fallback in case it's undefined. |
| 238 | * |
| 239 | * @param string $url Site Icon URL. |
| 240 | * @return string Site Icon URL. |
| 241 | */ |
| 242 | function wpcomsh_site_icon_url( $url ) { |
| 243 | if ( empty( $url ) ) { |
| 244 | $url = 'https://s0.wp.com/i/webclip.png'; |
| 245 | } |
| 246 | |
| 247 | return $url; |
| 248 | } |
| 249 | add_filter( 'get_site_icon_url', 'wpcomsh_site_icon_url' ); |