| 1 | <?php |
|---|
| 2 | /** |
|---|
| 3 | * WordPress Theme Administration API |
|---|
| 4 | * |
|---|
| 5 | * @package WordPress |
|---|
| 6 | * @subpackage Administration |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | /** |
|---|
| 10 | * {@internal Missing Short Description}} |
|---|
| 11 | * |
|---|
| 12 | * @since 2.0.0 |
|---|
| 13 | * |
|---|
| 14 | * @return unknown |
|---|
| 15 | */ |
|---|
| 16 | function current_theme_info() { |
|---|
| 17 | $themes = get_themes(); |
|---|
| 18 | $current_theme = get_current_theme(); |
|---|
| 19 | |
|---|
| 20 | if ( ! $themes ) { |
|---|
| 21 | $ct = new stdClass; |
|---|
| 22 | $ct->name = $current_theme; |
|---|
| 23 | return $ct; |
|---|
| 24 | } |
|---|
| 25 | |
|---|
| 26 | if ( ! isset( $themes[$current_theme] ) ) { |
|---|
| 27 | delete_option( 'current_theme' ); |
|---|
| 28 | $current_theme = get_current_theme(); |
|---|
| 29 | } |
|---|
| 30 | |
|---|
| 31 | $ct = new stdClass; |
|---|
| 32 | $ct->name = $current_theme; |
|---|
| 33 | $ct->title = $themes[$current_theme]['Title']; |
|---|
| 34 | $ct->version = $themes[$current_theme]['Version']; |
|---|
| 35 | $ct->parent_theme = $themes[$current_theme]['Parent Theme']; |
|---|
| 36 | $ct->template_dir = $themes[$current_theme]['Template Dir']; |
|---|
| 37 | $ct->stylesheet_dir = $themes[$current_theme]['Stylesheet Dir']; |
|---|
| 38 | $ct->template = $themes[$current_theme]['Template']; |
|---|
| 39 | $ct->stylesheet = $themes[$current_theme]['Stylesheet']; |
|---|
| 40 | $ct->screenshot = $themes[$current_theme]['Screenshot']; |
|---|
| 41 | $ct->description = $themes[$current_theme]['Description']; |
|---|
| 42 | $ct->author = $themes[$current_theme]['Author']; |
|---|
| 43 | $ct->tags = $themes[$current_theme]['Tags']; |
|---|
| 44 | $ct->theme_root = $themes[$current_theme]['Theme Root']; |
|---|
| 45 | $ct->theme_root_uri = $themes[$current_theme]['Theme Root URI']; |
|---|
| 46 | return $ct; |
|---|
| 47 | } |
|---|
| 48 | |
|---|
| 49 | /** |
|---|
| 50 | * Remove a theme |
|---|
| 51 | * |
|---|
| 52 | * @since 2.8.0 |
|---|
| 53 | * |
|---|
| 54 | * @param string $template Template directory of the theme to delete |
|---|
| 55 | * @param string $redirect Redirect to page when complete. |
|---|
| 56 | * @return mixed |
|---|
| 57 | */ |
|---|
| 58 | function delete_theme($template, $redirect = '') { |
|---|
| 59 | global $wp_filesystem; |
|---|
| 60 | |
|---|
| 61 | if ( empty($template) ) |
|---|
| 62 | return false; |
|---|
| 63 | |
|---|
| 64 | ob_start(); |
|---|
| 65 | if ( empty( $redirect ) ) |
|---|
| 66 | $redirect = wp_nonce_url('themes.php?action=delete&template=' . $template, 'delete-theme_' . $template); |
|---|
| 67 | if ( false === ($credentials = request_filesystem_credentials($redirect)) ) { |
|---|
| 68 | $data = ob_get_contents(); |
|---|
| 69 | ob_end_clean(); |
|---|
| 70 | if ( ! empty($data) ){ |
|---|
| 71 | include_once( ABSPATH . 'wp-admin/admin-header.php'); |
|---|
| 72 | echo $data; |
|---|
| 73 | include( ABSPATH . 'wp-admin/admin-footer.php'); |
|---|
| 74 | exit; |
|---|
| 75 | } |
|---|
| 76 | return; |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | if ( ! WP_Filesystem($credentials) ) { |
|---|
| 80 | request_filesystem_credentials($url, '', true); // Failed to connect, Error and request again |
|---|
| 81 | $data = ob_get_contents(); |
|---|
| 82 | ob_end_clean(); |
|---|
| 83 | if ( ! empty($data) ) { |
|---|
| 84 | include_once( ABSPATH . 'wp-admin/admin-header.php'); |
|---|
| 85 | echo $data; |
|---|
| 86 | include( ABSPATH . 'wp-admin/admin-footer.php'); |
|---|
| 87 | exit; |
|---|
| 88 | } |
|---|
| 89 | return; |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | |
|---|
| 93 | if ( ! is_object($wp_filesystem) ) |
|---|
| 94 | return new WP_Error('fs_unavailable', __('Could not access filesystem.')); |
|---|
| 95 | |
|---|
| 96 | if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() ) |
|---|
| 97 | return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors); |
|---|
| 98 | |
|---|
| 99 | //Get the base plugin folder |
|---|
| 100 | $themes_dir = $wp_filesystem->wp_themes_dir(); |
|---|
| 101 | if ( empty($themes_dir) ) |
|---|
| 102 | return new WP_Error('fs_no_themes_dir', __('Unable to locate WordPress theme directory.')); |
|---|
| 103 | |
|---|
| 104 | $themes_dir = trailingslashit( $themes_dir ); |
|---|
| 105 | $theme_dir = trailingslashit($themes_dir . $template); |
|---|
| 106 | $deleted = $wp_filesystem->delete($theme_dir, true); |
|---|
| 107 | |
|---|
| 108 | if ( ! $deleted ) |
|---|
| 109 | return new WP_Error('could_not_remove_theme', sprintf(__('Could not fully remove the theme %s.'), $template) ); |
|---|
| 110 | |
|---|
| 111 | // Force refresh of theme update information |
|---|
| 112 | delete_site_transient('update_themes'); |
|---|
| 113 | |
|---|
| 114 | return true; |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | /** |
|---|
| 118 | * {@internal Missing Short Description}} |
|---|
| 119 | * |
|---|
| 120 | * @since 1.5.0 |
|---|
| 121 | * |
|---|
| 122 | * @return unknown |
|---|
| 123 | */ |
|---|
| 124 | function get_broken_themes() { |
|---|
| 125 | global $wp_broken_themes; |
|---|
| 126 | |
|---|
| 127 | get_themes(); |
|---|
| 128 | return $wp_broken_themes; |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | /** |
|---|
| 132 | * Get the allowed themes for the current blog. |
|---|
| 133 | * |
|---|
| 134 | * @since 3.0.0 |
|---|
| 135 | * |
|---|
| 136 | * @uses get_themes() |
|---|
| 137 | * @uses current_theme_info() |
|---|
| 138 | * @uses get_site_allowed_themes() |
|---|
| 139 | * @uses wpmu_get_blog_allowedthemes |
|---|
| 140 | * |
|---|
| 141 | * @return array $themes Array of allowed themes. |
|---|
| 142 | */ |
|---|
| 143 | function get_allowed_themes() { |
|---|
| 144 | if ( !is_multisite() ) |
|---|
| 145 | return get_themes(); |
|---|
| 146 | |
|---|
| 147 | $themes = get_themes(); |
|---|
| 148 | $ct = current_theme_info(); |
|---|
| 149 | $allowed_themes = apply_filters("allowed_themes", get_site_allowed_themes() ); |
|---|
| 150 | if ( $allowed_themes == false ) |
|---|
| 151 | $allowed_themes = array(); |
|---|
| 152 | |
|---|
| 153 | $blog_allowed_themes = wpmu_get_blog_allowedthemes(); |
|---|
| 154 | if ( is_array( $blog_allowed_themes ) ) |
|---|
| 155 | $allowed_themes = array_merge( $allowed_themes, $blog_allowed_themes ); |
|---|
| 156 | |
|---|
| 157 | if ( isset( $allowed_themes[ esc_html( $ct->stylesheet ) ] ) == false ) |
|---|
| 158 | $allowed_themes[ esc_html( $ct->stylesheet ) ] = true; |
|---|
| 159 | |
|---|
| 160 | reset( $themes ); |
|---|
| 161 | foreach ( $themes as $key => $theme ) { |
|---|
| 162 | if ( isset( $allowed_themes[ esc_html( $theme[ 'Stylesheet' ] ) ] ) == false ) |
|---|
| 163 | unset( $themes[ $key ] ); |
|---|
| 164 | } |
|---|
| 165 | reset( $themes ); |
|---|
| 166 | |
|---|
| 167 | return $themes; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | /** |
|---|
| 171 | * Get the Page Templates available in this theme |
|---|
| 172 | * |
|---|
| 173 | * @since 1.5.0 |
|---|
| 174 | * |
|---|
| 175 | * @return array Key is the template name, value is the filename of the template |
|---|
| 176 | */ |
|---|
| 177 | function get_page_templates() { |
|---|
| 178 | $themes = get_themes(); |
|---|
| 179 | $theme = get_current_theme(); |
|---|
| 180 | $templates = $themes[$theme]['Template Files']; |
|---|
| 181 | $page_templates = array(); |
|---|
| 182 | |
|---|
| 183 | if ( is_array( $templates ) ) { |
|---|
| 184 | |
|---|
| 185 | foreach ( $templates as $template ) { |
|---|
| 186 | $basename = basename($template); |
|---|
| 187 | |
|---|
| 188 | // don't allow template files in subdirectories |
|---|
| 189 | if ( false !== strpos($basename, '/') ) |
|---|
| 190 | continue; |
|---|
| 191 | |
|---|
| 192 | if ( 'functions.php' == $basename ) |
|---|
| 193 | continue; |
|---|
| 194 | |
|---|
| 195 | $template_data = implode( '', file( $template )); |
|---|
| 196 | |
|---|
| 197 | $name = ''; |
|---|
| 198 | if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ) ) |
|---|
| 199 | $name = _cleanup_header_comment($name[1]); |
|---|
| 200 | |
|---|
| 201 | if ( !empty( $name ) ) { |
|---|
| 202 | $page_templates[trim( $name )] = $basename; |
|---|
| 203 | } |
|---|
| 204 | } |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | return $page_templates; |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | /** |
|---|
| 211 | * Tidies a filename for url display by the theme editor. |
|---|
| 212 | * |
|---|
| 213 | * @since 2.9.0 |
|---|
| 214 | * @access private |
|---|
| 215 | * |
|---|
| 216 | * @param string $fullpath Full path to the theme file |
|---|
| 217 | * @param string $containingfolder Path of the theme parent folder |
|---|
| 218 | * @return string |
|---|
| 219 | */ |
|---|
| 220 | function _get_template_edit_filename($fullpath, $containingfolder) { |
|---|
| 221 | return str_replace(dirname(dirname( $containingfolder )) , '', $fullpath); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | /** |
|---|
| 225 | * Check if there is an update for a theme available. |
|---|
| 226 | * |
|---|
| 227 | * Will display link, if there is an update available. |
|---|
| 228 | * |
|---|
| 229 | * @since 2.7.0 |
|---|
| 230 | * |
|---|
| 231 | * @param object $theme Theme data object. |
|---|
| 232 | * @return bool False if no valid info was passed. |
|---|
| 233 | */ |
|---|
| 234 | function theme_update_available( $theme ) { |
|---|
| 235 | static $themes_update; |
|---|
| 236 | |
|---|
| 237 | if ( !current_user_can('update_themes' ) ) |
|---|
| 238 | return; |
|---|
| 239 | |
|---|
| 240 | if ( !isset($themes_update) ) |
|---|
| 241 | $themes_update = get_site_transient('update_themes'); |
|---|
| 242 | |
|---|
| 243 | if ( is_object($theme) && isset($theme->stylesheet) ) |
|---|
| 244 | $stylesheet = $theme->stylesheet; |
|---|
| 245 | elseif ( is_array($theme) && isset($theme['Stylesheet']) ) |
|---|
| 246 | $stylesheet = $theme['Stylesheet']; |
|---|
| 247 | else |
|---|
| 248 | return false; //No valid info passed. |
|---|
| 249 | |
|---|
| 250 | if ( isset($themes_update->response[ $stylesheet ]) ) { |
|---|
| 251 | $update = $themes_update->response[ $stylesheet ]; |
|---|
| 252 | $theme_name = is_object($theme) ? $theme->name : (is_array($theme) ? $theme['Name'] : ''); |
|---|
| 253 | $details_url = add_query_arg(array('TB_iframe' => 'true', 'width' => 1024, 'height' => 800), $update['url']); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list. |
|---|
| 254 | $update_url = wp_nonce_url('update.php?action=upgrade-theme&theme=' . urlencode($stylesheet), 'upgrade-theme_' . $stylesheet); |
|---|
| 255 | $update_onclick = 'onclick="if ( confirm(\'' . esc_js( __("Updating this theme will lose any customizations you have made. 'Cancel' to stop, 'OK' to update.") ) . '\') ) {return true;}return false;"'; |
|---|
| 256 | |
|---|
| 257 | if ( !is_multisite() ) { |
|---|
| 258 | if ( ! current_user_can('update_themes') ) |
|---|
| 259 | printf( '<p><strong>' . __('There is a new version of %1$s available. <a href="%2$s" class="thickbox" title="%1$s">View version %3$s details</a>.') . '</strong></p>', $theme_name, $details_url, $update['new_version']); |
|---|
| 260 | else if ( empty($update['package']) ) |
|---|
| 261 | printf( '<p><strong>' . __('There is a new version of %1$s available. <a href="%2$s" class="thickbox" title="%1$s">View version %3$s details</a>. <em>Automatic update is unavailable for this theme.</em>') . '</strong></p>', $theme_name, $details_url, $update['new_version']); |
|---|
| 262 | else |
|---|
| 263 | printf( '<p><strong>' . __('There is a new version of %1$s available. <a href="%2$s" class="thickbox" title="%1$s">View version %3$s details</a> or <a href="%4$s" %5$s>update automatically</a>.') . '</strong></p>', $theme_name, $details_url, $update['new_version'], $update_url, $update_onclick ); |
|---|
| 264 | } |
|---|
| 265 | } |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | /** |
|---|
| 269 | * Retrieve list of WordPress theme features (aka theme tags) |
|---|
| 270 | * |
|---|
| 271 | * @since 3.1.0 |
|---|
| 272 | * |
|---|
| 273 | * @return array Array of features keyed by category with translations keyed by slug. |
|---|
| 274 | */ |
|---|
| 275 | function get_theme_feature_list() { |
|---|
| 276 | // Hard-coded list is used if api not accessible. |
|---|
| 277 | $features = array( |
|---|
| 278 | __('Colors') => array( |
|---|
| 279 | 'black' => __( 'Black' ), |
|---|
| 280 | 'blue' => __( 'Blue' ), |
|---|
| 281 | 'brown' => __( 'Brown' ), |
|---|
| 282 | 'gray' => __( 'Gray' ), |
|---|
| 283 | 'green' => __( 'Green' ), |
|---|
| 284 | 'orange' => __( 'Orange' ), |
|---|
| 285 | 'pink' => __( 'Pink' ), |
|---|
| 286 | 'purple' => __( 'Purple' ), |
|---|
| 287 | 'red' => __( 'Red' ), |
|---|
| 288 | 'silver' => __( 'Silver' ), |
|---|
| 289 | 'tan' => __( 'Tan' ), |
|---|
| 290 | 'white' => __( 'White' ), |
|---|
| 291 | 'yellow' => __( 'Yellow' ), |
|---|
| 292 | 'dark' => __( 'Dark' ), |
|---|
| 293 | 'light' => __( 'Light ') |
|---|
| 294 | ), |
|---|
| 295 | |
|---|
| 296 | __('Columns') => array( |
|---|
| 297 | 'one-column' => __( 'One Column' ), |
|---|
| 298 | 'two-columns' => __( 'Two Columns' ), |
|---|
| 299 | 'three-columns' => __( 'Three Columns' ), |
|---|
| 300 | 'four-columns' => __( 'Four Columns' ), |
|---|
| 301 | 'left-sidebar' => __( 'Left Sidebar' ), |
|---|
| 302 | 'right-sidebar' => __( 'Right Sidebar' ) |
|---|
| 303 | ), |
|---|
| 304 | |
|---|
| 305 | __('Width') => array( |
|---|
| 306 | 'fixed-width' => __( 'Fixed Width' ), |
|---|
| 307 | 'flexible-width' => __( 'Flexible Width' ) |
|---|
| 308 | ), |
|---|
| 309 | |
|---|
| 310 | __( 'Features' ) => array( |
|---|
| 311 | 'blavatar' => __( 'Blavatar' ), |
|---|
| 312 | 'buddypress' => __( 'BuddyPress' ), |
|---|
| 313 | 'custom-background' => __( 'Custom Background' ), |
|---|
| 314 | 'custom-colors' => __( 'Custom Colors' ), |
|---|
| 315 | 'custom-header' => __( 'Custom Header' ), |
|---|
| 316 | 'custom-menu' => __( 'Custom Menu' ), |
|---|
| 317 | 'editor-style' => __( 'Editor Style' ), |
|---|
| 318 | 'featured-image-header' => __( 'Featured Image Header' ), |
|---|
| 319 | 'featured-images' => __( 'Featured Images' ), |
|---|
| 320 | 'front-page-post-form' => __( 'Front Page Posting' ), |
|---|
| 321 | 'full-width-template' => __( 'Full Width Template' ), |
|---|
| 322 | 'microformats' => __( 'Microformats' ), |
|---|
| 323 | 'post-formats' => __( 'Post Formats' ), |
|---|
| 324 | 'rtl-language-support' => __( 'RTL Language Support' ), |
|---|
| 325 | 'sticky-post' => __( 'Sticky Post' ), |
|---|
| 326 | 'theme-options' => __( 'Theme Options' ), |
|---|
| 327 | 'threaded-comments' => __( 'Threaded Comments' ), |
|---|
| 328 | 'translation-ready' => __( 'Translation Ready' ) |
|---|
| 329 | ), |
|---|
| 330 | |
|---|
| 331 | __( 'Subject' ) => array( |
|---|
| 332 | 'holiday' => __( 'Holiday' ), |
|---|
| 333 | 'photoblogging' => __( 'Photoblogging' ), |
|---|
| 334 | 'seasonal' => __( 'Seasonal' ) |
|---|
| 335 | ) |
|---|
| 336 | ); |
|---|
| 337 | |
|---|
| 338 | if ( !current_user_can('install_themes') ) |
|---|
| 339 | return $features; |
|---|
| 340 | |
|---|
| 341 | if ( !$feature_list = get_site_transient( 'wporg_theme_feature_list' ) ) |
|---|
| 342 | set_site_transient( 'wporg_theme_feature_list', array( ), 10800); |
|---|
| 343 | |
|---|
| 344 | if ( !$feature_list ) { |
|---|
| 345 | $feature_list = themes_api( 'feature_list', array( ) ); |
|---|
| 346 | if ( is_wp_error( $feature_list ) ) |
|---|
| 347 | return $features; |
|---|
| 348 | } |
|---|
| 349 | |
|---|
| 350 | if ( !$feature_list ) |
|---|
| 351 | return $features; |
|---|
| 352 | |
|---|
| 353 | set_site_transient( 'wporg_theme_feature_list', $feature_list, 10800 ); |
|---|
| 354 | |
|---|
| 355 | $category_translations = array( 'Colors' => __('Colors'), 'Columns' => __('Columns'), 'Width' => __('Width'), |
|---|
| 356 | 'Features' => __('Features'), 'Subject' => __('Subject') ); |
|---|
| 357 | |
|---|
| 358 | // Loop over the wporg canonical list and apply translations |
|---|
| 359 | $wporg_features = array(); |
|---|
| 360 | foreach ( (array) $feature_list as $feature_category => $feature_items ) { |
|---|
| 361 | if ( isset($category_translations[$feature_category]) ) |
|---|
| 362 | $feature_category = $category_translations[$feature_category]; |
|---|
| 363 | $wporg_features[$feature_category] = array(); |
|---|
| 364 | |
|---|
| 365 | foreach ( $feature_items as $feature ) { |
|---|
| 366 | if ( isset($features[$feature_category][$feature]) ) |
|---|
| 367 | $wporg_features[$feature_category][$feature] = $features[$feature_category][$feature]; |
|---|
| 368 | else |
|---|
| 369 | $wporg_features[$feature_category][$feature] = $feature; |
|---|
| 370 | } |
|---|
| 371 | } |
|---|
| 372 | |
|---|
| 373 | return $wporg_features; |
|---|
| 374 | } |
|---|
| 375 | |
|---|
| 376 | /** |
|---|
| 377 | * Retrieve theme installer pages from WordPress Themes API. |
|---|
| 378 | * |
|---|
| 379 | * It is possible for a theme to override the Themes API result with three |
|---|
| 380 | * filters. Assume this is for themes, which can extend on the Theme Info to |
|---|
| 381 | * offer more choices. This is very powerful and must be used with care, when |
|---|
| 382 | * overridding the filters. |
|---|
| 383 | * |
|---|
| 384 | * The first filter, 'themes_api_args', is for the args and gives the action as |
|---|
| 385 | * the second parameter. The hook for 'themes_api_args' must ensure that an |
|---|
| 386 | * object is returned. |
|---|
| 387 | * |
|---|
| 388 | * The second filter, 'themes_api', is the result that would be returned. |
|---|
| 389 | * |
|---|
| 390 | * @since 2.8.0 |
|---|
| 391 | * |
|---|
| 392 | * @param string $action |
|---|
| 393 | * @param array|object $args Optional. Arguments to serialize for the Theme Info API. |
|---|
| 394 | * @return mixed |
|---|
| 395 | */ |
|---|
| 396 | function themes_api($action, $args = null) { |
|---|
| 397 | |
|---|
| 398 | if ( is_array($args) ) |
|---|
| 399 | $args = (object)$args; |
|---|
| 400 | |
|---|
| 401 | if ( !isset($args->per_page) ) |
|---|
| 402 | $args->per_page = 24; |
|---|
| 403 | |
|---|
| 404 | $args = apply_filters('themes_api_args', $args, $action); //NOTE: Ensure that an object is returned via this filter. |
|---|
| 405 | $res = apply_filters('themes_api', false, $action, $args); //NOTE: Allows a theme to completely override the builtin WordPress.org API. |
|---|
| 406 | |
|---|
| 407 | if ( ! $res ) { |
|---|
| 408 | $request = wp_remote_post('http://api.wordpress.org/themes/info/1.0/', array( 'body' => array('action' => $action, 'request' => serialize($args))) ); |
|---|
| 409 | if ( is_wp_error($request) ) { |
|---|
| 410 | $res = new WP_Error('themes_api_failed', __('An Unexpected HTTP Error occurred during the API request.'), $request->get_error_message() ); |
|---|
| 411 | } else { |
|---|
| 412 | $res = unserialize( wp_remote_retrieve_body( $request ) ); |
|---|
| 413 | if ( ! $res ) |
|---|
| 414 | $res = new WP_Error('themes_api_failed', __('An unknown error occurred.'), wp_remote_retrieve_body( $request ) ); |
|---|
| 415 | } |
|---|
| 416 | } |
|---|
| 417 | //var_dump(array($args, $res)); |
|---|
| 418 | return apply_filters('themes_api_result', $res, $action, $args); |
|---|
| 419 | } |
|---|
| 420 | |
|---|
| 421 | ?> |
|---|