~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-admin/includes/theme.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * WordPress Theme Administration API
 
4
 *
 
5
 * @package WordPress
 
6
 * @subpackage Administration
 
7
 */
 
8
 
 
9
/**
 
10
 * Remove a theme
 
11
 *
 
12
 * @since 2.8.0
 
13
 *
 
14
 * @param string $stylesheet Stylesheet of the theme to delete
 
15
 * @param string $redirect Redirect to page when complete.
 
16
 * @return mixed
 
17
 */
 
18
function delete_theme($stylesheet, $redirect = '') {
 
19
        global $wp_filesystem;
 
20
 
 
21
        if ( empty($stylesheet) )
 
22
                return false;
 
23
 
 
24
        ob_start();
 
25
        if ( empty( $redirect ) )
 
26
                $redirect = wp_nonce_url('themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet);
 
27
        if ( false === ($credentials = request_filesystem_credentials($redirect)) ) {
 
28
                $data = ob_get_contents();
 
29
                ob_end_clean();
 
30
                if ( ! empty($data) ){
 
31
                        include_once( ABSPATH . 'wp-admin/admin-header.php');
 
32
                        echo $data;
 
33
                        include( ABSPATH . 'wp-admin/admin-footer.php');
 
34
                        exit;
 
35
                }
 
36
                return;
 
37
        }
 
38
 
 
39
        if ( ! WP_Filesystem($credentials) ) {
 
40
                request_filesystem_credentials($redirect, '', true); // Failed to connect, Error and request again
 
41
                $data = ob_get_contents();
 
42
                ob_end_clean();
 
43
                if ( ! empty($data) ) {
 
44
                        include_once( ABSPATH . 'wp-admin/admin-header.php');
 
45
                        echo $data;
 
46
                        include( ABSPATH . 'wp-admin/admin-footer.php');
 
47
                        exit;
 
48
                }
 
49
                return;
 
50
        }
 
51
 
 
52
        if ( ! is_object($wp_filesystem) )
 
53
                return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
 
54
 
 
55
        if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
 
56
                return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors);
 
57
 
 
58
        //Get the base plugin folder
 
59
        $themes_dir = $wp_filesystem->wp_themes_dir();
 
60
        if ( empty($themes_dir) )
 
61
                return new WP_Error('fs_no_themes_dir', __('Unable to locate WordPress theme directory.'));
 
62
 
 
63
        $themes_dir = trailingslashit( $themes_dir );
 
64
        $theme_dir = trailingslashit($themes_dir . $stylesheet);
 
65
        $deleted = $wp_filesystem->delete($theme_dir, true);
 
66
 
 
67
        if ( ! $deleted )
 
68
                return new WP_Error('could_not_remove_theme', sprintf(__('Could not fully remove the theme %s.'), $stylesheet) );
 
69
 
 
70
        // Force refresh of theme update information
 
71
        delete_site_transient('update_themes');
 
72
 
 
73
        return true;
 
74
}
 
75
 
 
76
/**
 
77
 * Get the Page Templates available in this theme
 
78
 *
 
79
 * @since 1.5.0
 
80
 *
 
81
 * @param WP_Post|null $post Optional. The post being edited, provided for context.
 
82
 * @return array Key is the template name, value is the filename of the template
 
83
 */
 
84
function get_page_templates( $post = null ) {
 
85
        return array_flip( wp_get_theme()->get_page_templates( $post ) );
 
86
}
 
87
 
 
88
/**
 
89
 * Tidies a filename for url display by the theme editor.
 
90
 *
 
91
 * @since 2.9.0
 
92
 * @access private
 
93
 *
 
94
 * @param string $fullpath Full path to the theme file
 
95
 * @param string $containingfolder Path of the theme parent folder
 
96
 * @return string
 
97
 */
 
98
function _get_template_edit_filename($fullpath, $containingfolder) {
 
99
        return str_replace(dirname(dirname( $containingfolder )) , '', $fullpath);
 
100
}
 
101
 
 
102
/**
 
103
 * Check if there is an update for a theme available.
 
104
 *
 
105
 * Will display link, if there is an update available.
 
106
 *
 
107
 * @since 2.7.0
 
108
 * @see get_theme_update_available()
 
109
 *
 
110
 * @param object $theme Theme data object.
 
111
 */
 
112
function theme_update_available( $theme ) {
 
113
        echo get_theme_update_available( $theme );
 
114
}
 
115
 
 
116
/**
 
117
 * Retrieve the update link if there is a theme update available.
 
118
 *
 
119
 * Will return a link if there is an update available.
 
120
 *
 
121
 * @since 3.8.0
 
122
 *
 
123
 * @param WP_Theme $theme WP_Theme object.
 
124
 * @return string|bool HTML for the update link, or false if invalid info was passed.
 
125
 */
 
126
function get_theme_update_available( $theme ) {
 
127
        static $themes_update;
 
128
 
 
129
        if ( !current_user_can('update_themes' ) )
 
130
                return false;
 
131
 
 
132
        if ( !isset($themes_update) )
 
133
                $themes_update = get_site_transient('update_themes');
 
134
 
 
135
        if ( ! is_a( $theme, 'WP_Theme' ) )
 
136
                return false;
 
137
 
 
138
        $stylesheet = $theme->get_stylesheet();
 
139
 
 
140
        $html = '';
 
141
 
 
142
        if ( isset($themes_update->response[ $stylesheet ]) ) {
 
143
                $update = $themes_update->response[ $stylesheet ];
 
144
                $theme_name = $theme->display('Name');
 
145
                $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.
 
146
                $update_url = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&amp;theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet );
 
147
                $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;"';
 
148
 
 
149
                if ( !is_multisite() ) {
 
150
                        if ( ! current_user_can('update_themes') ) {
 
151
                                $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox" title="%3$s">View version %4$s details</a>.' ) . '</strong></p>',
 
152
                                        $theme_name, esc_url( $details_url ), esc_attr( $theme['Name'] ), $update['new_version'] );
 
153
                        } else if ( empty( $update['package'] ) ) {
 
154
                                $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox" title="%3$s">View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ) . '</strong></p>',
 
155
                                        $theme_name, esc_url( $details_url ), esc_attr( $theme['Name'] ), $update['new_version'] );
 
156
                        } else {
 
157
                                $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox" title="%3$s">View version %4$s details</a> or <a href="%5$s">update now</a>.' ) . '</strong></p>',
 
158
                                        $theme_name, esc_url( $details_url ), esc_attr( $theme['Name'] ), $update['new_version'], $update_url, $update_onclick );
 
159
                        }
 
160
                }
 
161
        }
 
162
 
 
163
        return $html;
 
164
}
 
165
 
 
166
/**
 
167
 * Retrieve list of WordPress theme features (aka theme tags)
 
168
 *
 
169
 * @since 3.1.0
 
170
 *
 
171
 * @param bool $api Optional. Whether try to fetch tags from the WP.org API. Defaults to true.
 
172
 * @return array Array of features keyed by category with translations keyed by slug.
 
173
 */
 
174
function get_theme_feature_list( $api = true ) {
 
175
        // Hard-coded list is used if api not accessible.
 
176
        $features = array(
 
177
                        __( 'Colors' ) => array(
 
178
                                'black'   => __( 'Black' ),
 
179
                                'blue'    => __( 'Blue' ),
 
180
                                'brown'   => __( 'Brown' ),
 
181
                                'gray'    => __( 'Gray' ),
 
182
                                'green'   => __( 'Green' ),
 
183
                                'orange'  => __( 'Orange' ),
 
184
                                'pink'    => __( 'Pink' ),
 
185
                                'purple'  => __( 'Purple' ),
 
186
                                'red'     => __( 'Red' ),
 
187
                                'silver'  => __( 'Silver' ),
 
188
                                'tan'     => __( 'Tan' ),
 
189
                                'white'   => __( 'White' ),
 
190
                                'yellow'  => __( 'Yellow' ),
 
191
                                'dark'    => __( 'Dark' ),
 
192
                                'light'   => __( 'Light' ),
 
193
                        ),
 
194
 
 
195
                __( 'Layout' ) => array(
 
196
                        'fixed-layout'      => __( 'Fixed Layout' ),
 
197
                        'fluid-layout'      => __( 'Fluid Layout' ),
 
198
                        'responsive-layout' => __( 'Responsive Layout' ),
 
199
                        'one-column'    => __( 'One Column' ),
 
200
                        'two-columns'   => __( 'Two Columns' ),
 
201
                        'three-columns' => __( 'Three Columns' ),
 
202
                        'four-columns'  => __( 'Four Columns' ),
 
203
                        'left-sidebar'  => __( 'Left Sidebar' ),
 
204
                        'right-sidebar' => __( 'Right Sidebar' ),
 
205
                ),
 
206
 
 
207
                __( 'Features' ) => array(
 
208
                        'accessibility-ready'   => __( 'Accessibility Ready' ),
 
209
                        'blavatar'              => __( 'Blavatar' ),
 
210
                        'buddypress'            => __( 'BuddyPress' ),
 
211
                        'custom-background'     => __( 'Custom Background' ),
 
212
                        'custom-colors'         => __( 'Custom Colors' ),
 
213
                        'custom-header'         => __( 'Custom Header' ),
 
214
                        'custom-menu'           => __( 'Custom Menu' ),
 
215
                        'editor-style'          => __( 'Editor Style' ),
 
216
                        'featured-image-header' => __( 'Featured Image Header' ),
 
217
                        'featured-images'       => __( 'Featured Images' ),
 
218
                        'flexible-header'       => __( 'Flexible Header' ),
 
219
                        'front-page-post-form'  => __( 'Front Page Posting' ),
 
220
                        'full-width-template'   => __( 'Full Width Template' ),
 
221
                        'microformats'          => __( 'Microformats' ),
 
222
                        'post-formats'          => __( 'Post Formats' ),
 
223
                        'rtl-language-support'  => __( 'RTL Language Support' ),
 
224
                        'sticky-post'           => __( 'Sticky Post' ),
 
225
                        'theme-options'         => __( 'Theme Options' ),
 
226
                        'threaded-comments'     => __( 'Threaded Comments' ),
 
227
                        'translation-ready'     => __( 'Translation Ready' ),
 
228
                ),
 
229
 
 
230
                __( 'Subject' )  => array(
 
231
                        'holiday'       => __( 'Holiday' ),
 
232
                        'photoblogging' => __( 'Photoblogging' ),
 
233
                        'seasonal'      => __( 'Seasonal' ),
 
234
                )
 
235
        );
 
236
 
 
237
        if ( ! $api || ! current_user_can( 'install_themes' ) )
 
238
                return $features;
 
239
 
 
240
        if ( !$feature_list = get_site_transient( 'wporg_theme_feature_list' ) )
 
241
                set_site_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS );
 
242
 
 
243
        if ( !$feature_list ) {
 
244
                $feature_list = themes_api( 'feature_list', array() );
 
245
                if ( is_wp_error( $feature_list ) )
 
246
                        return $features;
 
247
        }
 
248
 
 
249
        if ( !$feature_list )
 
250
                return $features;
 
251
 
 
252
        set_site_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS );
 
253
 
 
254
        $category_translations = array(
 
255
                'Colors'   => __( 'Colors' ),
 
256
                'Layout'   => __( 'Layout' ),
 
257
                'Features' => __( 'Features' ),
 
258
                'Subject'  => __( 'Subject' )
 
259
        );
 
260
 
 
261
        // Loop over the wporg canonical list and apply translations
 
262
        $wporg_features = array();
 
263
        foreach ( (array) $feature_list as $feature_category => $feature_items ) {
 
264
                if ( isset($category_translations[$feature_category]) )
 
265
                        $feature_category = $category_translations[$feature_category];
 
266
                $wporg_features[$feature_category] = array();
 
267
 
 
268
                foreach ( $feature_items as $feature ) {
 
269
                        if ( isset($features[$feature_category][$feature]) )
 
270
                                $wporg_features[$feature_category][$feature] = $features[$feature_category][$feature];
 
271
                        else
 
272
                                $wporg_features[$feature_category][$feature] = $feature;
 
273
                }
 
274
        }
 
275
 
 
276
        return $wporg_features;
 
277
}
 
278
 
 
279
/**
 
280
 * Retrieve theme installer pages from WordPress Themes API.
 
281
 *
 
282
 * It is possible for a theme to override the Themes API result with three
 
283
 * filters. Assume this is for themes, which can extend on the Theme Info to
 
284
 * offer more choices. This is very powerful and must be used with care, when
 
285
 * overriding the filters.
 
286
 *
 
287
 * The first filter, 'themes_api_args', is for the args and gives the action as
 
288
 * the second parameter. The hook for 'themes_api_args' must ensure that an
 
289
 * object is returned.
 
290
 *
 
291
 * The second filter, 'themes_api', is the result that would be returned.
 
292
 *
 
293
 * @since 2.8.0
 
294
 *
 
295
 * @param string       $action The requested action. Likely values are 'theme_information',
 
296
 *                             'feature_list', or 'query_themes'.
 
297
 * @param array|object $args   Optional. Arguments to serialize for the Theme Info API.
 
298
 * @return mixed
 
299
 */
 
300
        function themes_api( $action, $args = null ) {
 
301
 
 
302
        if ( is_array($args) )
 
303
                $args = (object)$args;
 
304
 
 
305
        if ( !isset($args->per_page) )
 
306
                $args->per_page = 24;
 
307
        /**
 
308
         * Filter arguments used to query for installer pages from the WordPress.org Themes API.
 
309
         *
 
310
         * Important: An object MUST be returned to this filter.
 
311
         *
 
312
         * @since 2.8.0
 
313
         *
 
314
         * @param object $args   Arguments used to query for installer pages from the WordPress.org Themes API.
 
315
         * @param string $action Requested action. Likely values are 'theme_information',
 
316
         *                       'feature_list', or 'query_themes'.
 
317
        */
 
318
        $args = apply_filters( 'themes_api_args', $args, $action );
 
319
 
 
320
        /**
 
321
         * Filter whether to override the WordPress.org Themes API.
 
322
         *
 
323
         * Returning a value of true to this filter allows a theme to completely
 
324
         * override the built-in WordPress.org API.
 
325
         *
 
326
         * @since 2.8.0
 
327
         *
 
328
         * @param bool   $bool   Whether to override the WordPress.org Themes API. Default false.
 
329
         * @param string $action Requested action. Likely values are 'theme_information',
 
330
         *                       'feature_list', or 'query_themes'.
 
331
         * @param object $args   Arguments used to query for installer pages from the Themes API.
 
332
         */
 
333
        $res = apply_filters( 'themes_api', false, $action, $args );
 
334
 
 
335
        if ( ! $res ) {
 
336
                $url = $http_url = 'http://api.wordpress.org/themes/info/1.0/';
 
337
                if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
 
338
                        $url = set_url_scheme( $url, 'https' );
 
339
 
 
340
                $args = array(
 
341
                        'body' => array(
 
342
                                'action' => $action,
 
343
                                'request' => serialize( $args )
 
344
                        )
 
345
                );
 
346
                $request = wp_remote_post( $url, $args );
 
347
 
 
348
                if ( $ssl && is_wp_error( $request ) ) {
 
349
                        if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
 
350
                                trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE );
 
351
                        }
 
352
                        $request = wp_remote_post( $http_url, $args );
 
353
                }
 
354
 
 
355
                if ( is_wp_error($request) ) {
 
356
                        $res = new WP_Error('themes_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ), $request->get_error_message() );
 
357
                } else {
 
358
                        $res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
 
359
                        if ( ! is_object( $res ) && ! is_array( $res ) )
 
360
                                $res = new WP_Error('themes_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ), wp_remote_retrieve_body( $request ) );
 
361
                }
 
362
        }
 
363
 
 
364
        /**
 
365
         * Filter the returned WordPress.org Themes API response.
 
366
         *
 
367
         * @since 2.8.0
 
368
         *
 
369
         * @param array|object $res    WordPress.org Themes API response.
 
370
         * @param string       $action Requested action. Likely values are 'theme_information',
 
371
         *                             'feature_list', or 'query_themes'.
 
372
         * @param object       $args   Arguments used to query for installer pages from the WordPress.org Themes API.
 
373
         */
 
374
        return apply_filters( 'themes_api_result', $res, $action, $args );
 
375
}
 
376
 
 
377
/**
 
378
 * Prepare themes for JavaScript.
 
379
 *
 
380
 * @since 3.8.0
 
381
 *
 
382
 * @param array $themes Optional. Array of WP_Theme objects to prepare.
 
383
 *                      Defaults to all allowed themes.
 
384
 *
 
385
 * @return array An associative array of theme data, sorted by name.
 
386
 */
 
387
function wp_prepare_themes_for_js( $themes = null ) {
 
388
        $current_theme = get_stylesheet();
 
389
 
 
390
        // Make sure the current theme is listed first.
 
391
        $prepared_themes = array( $current_theme => array() );
 
392
 
 
393
        if ( null === $themes ) {
 
394
                $themes = wp_get_themes( array( 'allowed' => true ) );
 
395
                if ( ! isset( $themes[ $current_theme ] ) ) {
 
396
                        $themes[ $current_theme ] = wp_get_theme();
 
397
                }
 
398
        }
 
399
 
 
400
        $updates = array();
 
401
        if ( current_user_can( 'update_themes' ) ) {
 
402
                $updates_transient = get_site_transient( 'update_themes' );
 
403
                if ( isset( $updates_transient->response ) ) {
 
404
                        $updates = $updates_transient->response;
 
405
                }
 
406
        }
 
407
 
 
408
        WP_Theme::sort_by_name( $themes );
 
409
        foreach ( $themes as $theme ) {
 
410
                $parent = false;
 
411
                if ( $theme->parent() ) {
 
412
                        $parent = $theme->parent()->display( 'Name' );
 
413
                }
 
414
 
 
415
                $slug = $theme->get_stylesheet();
 
416
                $encoded_slug = urlencode( $slug );
 
417
 
 
418
                $prepared_themes[ $slug ] = array(
 
419
                        'id'           => $slug,
 
420
                        'name'         => $theme->display( 'Name' ),
 
421
                        'screenshot'   => array( $theme->get_screenshot() ), // @todo multiple
 
422
                        'description'  => $theme->display( 'Description' ),
 
423
                        'author'       => $theme->display( 'Author', false, true ),
 
424
                        'authorAndUri' => $theme->display( 'Author' ),
 
425
                        'version'      => $theme->display( 'Version' ),
 
426
                        'tags'         => $theme->display( 'Tags' ),
 
427
                        'parent'       => $parent,
 
428
                        'active'       => $slug === $current_theme,
 
429
                        'hasUpdate'    => isset( $updates[ $slug ] ),
 
430
                        'update'       => get_theme_update_available( $theme ),
 
431
                        'actions'      => array(
 
432
                                'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&amp;stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null,
 
433
                                'customize' => ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) ? wp_customize_url( $slug ) : null,
 
434
                                'preview'   => add_query_arg( array(
 
435
                                        'preview'        => 1,
 
436
                                        'template'       => urlencode( $theme->get_template() ),
 
437
                                        'stylesheet'     => urlencode( $slug ),
 
438
                                        'preview_iframe' => true,
 
439
                                        'TB_iframe'      => true,
 
440
                                ), home_url( '/' ) ),
 
441
                                'delete'   => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&amp;stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null,
 
442
                        ),
 
443
                );
 
444
        }
 
445
 
 
446
        /**
 
447
         * Filter the themes prepared for JavaScript, for themes.php.
 
448
         *
 
449
         * Could be useful for changing the order, which is by name by default.
 
450
         *
 
451
         * @since 3.8.0
 
452
         *
 
453
         * @param array $prepared_themes Array of themes.
 
454
         */
 
455
        $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes );
 
456
        return array_values( $prepared_themes );
 
457
}