Completed
Push — renovate/history-4.x ( 8706da...6c1ea7 )
by
unknown
17:57 queued 11:18
created

Jetpack_Custom_CSS_Enhancements::sanitize_css()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 69

Duplication

Lines 15
Ratio 21.74 %

Importance

Changes 0
Metric Value
cc 6
nc 9
nop 2
dl 15
loc 69
rs 8.0541
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
use Automattic\Jetpack\Assets;
4
5
/**
6
 * Alternate Custom CSS source for 4.7 compat.
7
 *
8
 * @since 4.4.2
9
 *
10
 * @package Jetpack
11
 */
12
13
/**
14
 * Class Jetpack_Custom_CSS_Enhancements
15
 */
16
class Jetpack_Custom_CSS_Enhancements {
17
18
	/**
19
	 * Set up the actions and filters needed for our compatability layer on top of core's Custom CSS implementation.
20
	 */
21
	public static function add_hooks() {
22
		add_action( 'init', array( __CLASS__, 'init' ) );
23
		add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) );
24
		add_action( 'customize_controls_enqueue_scripts', array( __CLASS__, 'customize_controls_enqueue_scripts' ) );
25
		add_action( 'customize_register', array( __CLASS__, 'customize_register' ) );
26
		add_filter( 'map_meta_cap', array( __CLASS__, 'map_meta_cap' ), 20, 2 );
27
		add_action( 'customize_preview_init', array( __CLASS__, 'customize_preview_init' ) );
28
		add_filter( '_wp_post_revision_fields', array( __CLASS__, '_wp_post_revision_fields' ), 10, 2 );
29
		add_action( 'load-revision.php', array( __CLASS__, 'load_revision_php' ) );
30
31
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'wp_enqueue_scripts' ) );
32
33
		// Handle Sass/LESS.
34
		add_filter( 'customize_value_custom_css', array( __CLASS__, 'customize_value_custom_css' ), 10, 2 );
35
		add_filter( 'customize_update_custom_css_post_content_args', array( __CLASS__, 'customize_update_custom_css_post_content_args' ), 10, 3 );
36
		add_filter( 'update_custom_css_data', array( __CLASS__, 'update_custom_css_data' ), 10, 2 );
37
38
		// Stuff for stripping out the theme's default stylesheet...
39
		add_filter( 'stylesheet_uri', array( __CLASS__, 'style_filter' ) );
40
		add_filter( 'safecss_skip_stylesheet', array( __CLASS__, 'preview_skip_stylesheet' ) );
41
42
		// Stuff for overriding content width...
43
		add_action( 'customize_preview_init', array( __CLASS__, 'preview_content_width' ) );
44
		add_filter( 'jetpack_content_width', array( __CLASS__, 'jetpack_content_width' ) );
45
		add_filter( 'editor_max_image_size', array( __CLASS__, 'editor_max_image_size' ), 10, 3 );
46
		add_action( 'template_redirect', array( __CLASS__, 'set_content_width' ) );
47
		add_action( 'admin_init', array( __CLASS__, 'set_content_width' ) );
48
49
		// Stuff?
50
	}
51
52
	/**
53
	 * Things that we do on init.
54
	 */
55
	public static function init() {
56
		$min = '.min';
0 ignored issues
show
Unused Code introduced by
$min is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
57
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
58
			$min = '';
0 ignored issues
show
Unused Code introduced by
$min is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
59
		}
60
61
		wp_register_style( 'jetpack-codemirror',      plugins_url( 'custom-css/css/codemirror.css', __FILE__ ), array(), '20120905' );
62
		wp_register_style( 'jetpack-customizer-css',  plugins_url( 'custom-css/css/customizer-control.css', __FILE__ ), array(), '20140728' );
63
		wp_register_script( 'jetpack-codemirror',     plugins_url( 'custom-css/js/codemirror.min.js', __FILE__ ), array(), '3.16', true );
64
65
		$src    = Assets::get_file_url_for_environment(
66
			'_inc/build/custom-css/custom-css/js/core-customizer-css.core-4.9.min.js',
67
			'modules/custom-css/custom-css/js/core-customizer-css.core-4.9.js'
68
		);
69
		wp_register_script( 'jetpack-customizer-css', $src, array(
70
			'customize-controls',
71
			'underscore'
72
		), JETPACK__VERSION, true );
73
74
		wp_register_script(
75
			'jetpack-customizer-css-preview',
76
			Assets::get_file_url_for_environment(
77
				'_inc/build/custom-css/custom-css/js/core-customizer-css-preview.min.js',
78
				'modules/custom-css/custom-css/js/core-customizer-css-preview.js'
79
			),
80
			array( 'customize-selective-refresh' ),
81
			JETPACK__VERSION,
82
			true
83
		);
84
85
		remove_action( 'wp_head', 'wp_custom_css_cb', 11 ); // 4.7.0 had it at 11, 4.7.1 moved it to 101.
86
		remove_action( 'wp_head', 'wp_custom_css_cb', 101 );
87
		add_action( 'wp_head', array( __CLASS__, 'wp_custom_css_cb' ), 101 );
88
89
		if ( isset( $_GET['custom-css'] ) ) {
90
			self::print_linked_custom_css();
91
		}
92
	}
93
94
	/**
95
	 * Things that we do on init when the Customize Preview is loading.
96
	 */
97
	public static function customize_preview_init() {
98
		add_filter( 'wp_get_custom_css', array( __CLASS__, 'customize_preview_wp_get_custom_css' ) );
99
	}
100
101
	/**
102
	 * Print the current Custom CSS. This is for linking instead of printing directly.
103
	 */
104
	public static function print_linked_custom_css() {
105
		header( 'Content-type: text/css' );
106
		header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + YEAR_IN_SECONDS ) . ' GMT' );
107
		echo wp_get_custom_css();
108
		exit;
109
	}
110
111
	/**
112
	 * Re-map the Edit CSS capability.
113
	 *
114
	 * Core, by default, restricts this to users that have `unfiltered_html` which
115
	 * would make the feature unusable in multi-site by non-super-admins, due to Core
116
	 * not shipping any solid sanitization.
117
	 *
118
	 * We're expanding who can use it, and then conditionally applying CSSTidy
119
	 * sanitization to users that do not have the `unfiltered_html` capability.
120
	 *
121
	 * @param array  $caps Returns the user's actual capabilities.
122
	 * @param string $cap  Capability name.
123
	 *
124
	 * @return array $caps
125
	 */
126
	public static function map_meta_cap( $caps, $cap ) {
127
		if ( 'edit_css' === $cap ) {
128
			$caps = array( 'edit_theme_options' );
129
		}
130
		return $caps;
131
	}
132
133
	/**
134
	 * Handle our admin menu item and legacy page declaration.
135
	 */
136
	public static function admin_menu() {
137
		// Add in our legacy page to support old bookmarks and such.
138
		add_submenu_page( null, __( 'CSS', 'jetpack' ), __( 'Edit CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'admin_page' ) );
139
140
		// Add in our new page slug that will redirect to the customizer.
141
		$hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Edit CSS', 'jetpack' ), 'edit_theme_options', 'editcss-customizer-redirect', array( __CLASS__, 'admin_page' ) );
142
		add_action( "load-{$hook}", array( __CLASS__, 'customizer_redirect' ) );
143
	}
144
145
	/**
146
	 * Handle the redirect for the customizer.  This is necessary because
147
	 * we can't directly add customizer links to the admin menu.
148
	 *
149
	 * There is a core patch in trac that would make this unnecessary.
150
	 *
151
	 * @link https://core.trac.wordpress.org/ticket/39050
152
	 */
153
	public static function customizer_redirect() {
154
		wp_safe_redirect( self::customizer_link( array(
155
			'return_url' => wp_get_referer(),
156
		) ) );
157
		exit;
158
	}
159
160
	/**
161
	 * Shows Preprocessor code in the Revisions screen, and ensures that post_content_filtered
162
	 * is maintained on revisions
163
	 *
164
	 * @param array $fields  Post fields pertinent to revisions.
165
	 * @param array $post    A post array being processed for insertion as a post revision.
166
	 *
167
	 * @return array $fields Modified array to include post_content_filtered.
168
	 */
169
	public static function _wp_post_revision_fields( $fields, $post ) {
170
		// None of the fields in $post are required to be passed in this filter.
171
		if ( ! isset( $post['post_type'], $post['ID'] ) ) {
172
			return $fields;
173
		}
174
175
		// If we're passed in a revision, go get the main post instead.
176
		if ( 'revision' === $post['post_type'] ) {
177
			$main_post_id = wp_is_post_revision( $post['ID'] );
178
			$post = get_post( $main_post_id, ARRAY_A );
179
		}
180
		if ( 'custom_css' === $post['post_type'] ) {
181
			$fields['post_content'] = __( 'CSS', 'jetpack' );
182
			$fields['post_content_filtered'] = __( 'Preprocessor', 'jetpack' );
183
		}
184
		return $fields;
185
	}
186
187
	/**
188
	 * Get the published custom CSS post.
189
	 *
190
	 * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme.
191
	 * @return WP_Post|null
192
	 */
193
	public static function get_css_post( $stylesheet = '' ) {
194
		return wp_get_custom_css_post( $stylesheet );
195
	}
196
197
	/**
198
	 * Override Core's `wp_custom_css_cb` method to provide linking to custom css.
199
	 */
200
	public static function wp_custom_css_cb() {
201
		$styles = wp_get_custom_css();
202
		if ( strlen( $styles ) > 2000 && ! is_customize_preview() ) :
203
			// Add a cache buster to the url.
204
			$url = home_url( '/' );
205
			$url = add_query_arg( 'custom-css', substr( md5( $styles ), -10 ), $url );
206
			?>
207
			<link rel="stylesheet" type="text/css" id="wp-custom-css" href="<?php echo esc_url( $url ); ?>" />
208
		<?php elseif ( $styles || is_customize_preview() ) : ?>
209
			<style type="text/css" id="wp-custom-css">
210
				<?php echo strip_tags( $styles ); // Note that esc_html() cannot be used because `div &gt; span` is not interpreted properly. ?>
211
			</style>
212
		<?php endif;
213
	}
214
215
	/**
216
	 * Get the ID of a Custom CSS post tying to a given stylesheet.
217
	 *
218
	 * @param string $stylesheet Stylesheet name.
219
	 *
220
	 * @return int $post_id Post ID.
221
	 */
222
	public static function post_id( $stylesheet = '' ) {
223
		$post = self::get_css_post( $stylesheet );
224
		if ( $post instanceof WP_Post ) {
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
225
			return $post->ID;
226
		}
227
		return 0;
228
	}
229
230
	/**
231
	 * Partial for use in the Customizer.
232
	 */
233
	public static function echo_custom_css_partial() {
234
		echo wp_get_custom_css();
235
	}
236
237
	/**
238
	 * Admin page!
239
	 *
240
	 * This currently has two main uses -- firstly to display the css for an inactive
241
	 * theme if there are no revisions attached it to a legacy bug, and secondly to
242
	 * handle folks that have bookmarkes in their browser going to the old page for
243
	 * managing Custom CSS in Jetpack.
244
	 *
245
	 * If we ever add back in a non-Customizer CSS editor, this would be the place.
246
	 */
247
	public static function admin_page() {
248
		$post = null;
249
		$stylesheet = null;
250
		if ( isset( $_GET['id'] ) ) {
251
			$post_id = absint( $_GET['id'] );
252
			$post = get_post( $post_id );
253
			if ( $post instanceof WP_Post && 'custom_css' === $post->post_type ) {
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
254
				$stylesheet = $post->post_title;
255
			}
256
		}
257
		?>
258
		<div class="wrap">
259
			<?php self::revisions_switcher_box( $stylesheet ); ?>
260
			<h1>
261
				<?php
262
				if ( $post ) {
263
					printf( 'Custom CSS for &#8220;%1$s&#8221;', wp_get_theme( $stylesheet )->Name );
264
				} else {
265
					esc_html_e( 'Custom CSS', 'jetpack' );
266
				}
267
				if ( current_user_can( 'customize' ) ) {
268
					printf(
269
						' <a class="page-title-action hide-if-no-customize" href="%1$s">%2$s</a>',
270
						esc_url( self::customizer_link() ),
271
						esc_html__( 'Manage with Live Preview', 'jetpack' )
272
					);
273
				}
274
				?>
275
			</h1>
276
			<p><?php esc_html_e( 'Custom CSS is now managed in the Customizer.', 'jetpack' ); ?></p>
277
			<?php if ( $post ) : ?>
278
				<div class="revisions">
279
					<h3><?php esc_html_e( 'CSS', 'jetpack' ); ?></h3>
280
					<textarea class="widefat" readonly><?php echo esc_textarea( $post->post_content ); ?></textarea>
281
					<?php if ( $post->post_content_filtered ) : ?>
282
						<h3><?php esc_html_e( 'Preprocessor', 'jetpack' ); ?></h3>
283
						<textarea class="widefat" readonly><?php echo esc_textarea( $post->post_content_filtered ); ?></textarea>
284
					<?php endif; ?>
285
				</div>
286
			<?php endif; ?>
287
		</div>
288
289
		<style>
290
			.other-themes-wrap {
291
				float: right;
292
				background-color: #fff;
293
				-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.1);
294
				box-shadow: 0 1px 3px rgba(0,0,0,0.1);
295
				padding: 5px 10px;
296
				margin-bottom: 10px;
297
			}
298
			.other-themes-wrap label {
299
				display: block;
300
				margin-bottom: 10px;
301
			}
302
			.other-themes-wrap select {
303
				float: left;
304
				width: 77%;
305
			}
306
			.other-themes-wrap button {
307
				float: right;
308
				width: 20%;
309
			}
310
			.revisions {
311
				clear: both;
312
			}
313
			.revisions textarea {
314
				min-height: 300px;
315
				background: #fff;
316
			}
317
		</style>
318
		<script>
319
			(function($){
320
				var $switcher = $('.other-themes-wrap');
321
				$switcher.find('button').on('click', function(e){
322
					e.preventDefault();
323
					if ( $switcher.find('select').val() ) {
324
						window.location.href = $switcher.find('select').val();
325
					}
326
				});
327
			})(jQuery);
328
		</script>
329
		<?php
330
	}
331
332
	/**
333
	 * Build the URL to deep link to the Customizer.
334
	 *
335
	 * You can modify the return url via $args.
336
	 *
337
	 * @param array $args Array of parameters.
338
	 * @return string
339
	 */
340
	public static function customizer_link( $args = array() ) {
341
		$args = wp_parse_args( $args, array(
0 ignored issues
show
Documentation introduced by
array('return_url' => ur...ERVER['REQUEST_URI']))) is of type array<string,string,{"return_url":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
342
			'return_url' => urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ),
343
		) );
344
345
		return add_query_arg(
346
			array(
347
				array(
348
					'autofocus' => array(
349
						'section' => 'custom_css',
350
					),
351
				),
352
				'return' => $args['return_url'],
353
			),
354
			admin_url( 'customize.php' )
355
		);
356
	}
357
358
	/**
359
	 * Handle the enqueueing and localizing for scripts to be used in the Customizer.
360
	 */
361
	public static function customize_controls_enqueue_scripts() {
362
		wp_enqueue_style( 'jetpack-customizer-css' );
363
		wp_enqueue_script( 'jetpack-customizer-css' );
364
365
		$content_help = __( 'Set a different content width for full size images.', 'jetpack' );
366
		if ( ! empty( $GLOBALS['content_width'] ) ) {
367
			$content_help .= sprintf(
368
				_n( ' The default content width for the <strong>%1$s</strong> theme is %2$d pixel.', ' The default content width for the <strong>%1$s</strong> theme is %2$d pixels.', intval( $GLOBALS['content_width'] ), 'jetpack' ),
369
				wp_get_theme()->Name,
370
				intval( $GLOBALS['content_width'] )
371
			);
372
		}
373
374
		wp_localize_script( 'jetpack-customizer-css', '_jp_css_settings', array(
375
			/** This filter is documented in modules/custom-css/custom-css.php */
376
			'useRichEditor' => ! jetpack_is_mobile() && apply_filters( 'safecss_use_ace', true ),
377
			'areThereCssRevisions' => self::are_there_css_revisions(),
378
			'revisionsUrl' => self::get_revisions_url(),
379
			'cssHelpUrl' => '//en.support.wordpress.com/custom-design/editing-css/',
380
			'l10n' => array(
381
				'mode'           => __( 'Start Fresh', 'jetpack' ),
382
				'mobile'         => __( 'On Mobile', 'jetpack' ),
383
				'contentWidth'   => $content_help,
384
				'revisions'      => _x( 'See full history', 'Toolbar button to see full CSS revision history', 'jetpack' ),
385
				'css_help_title' => _x( 'Help', 'Toolbar button to get help with custom CSS', 'jetpack' ),
386
			),
387
		));
388
	}
389
390
	/**
391
	 * Check whether there are CSS Revisions for a given theme.
392
	 *
393
	 * Going forward, there should always be, but this was necessitated
394
	 * early on by https://core.trac.wordpress.org/ticket/30854
395
	 *
396
	 * @param string $stylesheet Stylesheet name.
397
	 *
398
	 * @return bool|null|WP_Post
399
	 */
400
	public static function are_there_css_revisions( $stylesheet = '' ) {
401
		$post = wp_get_custom_css_post( $stylesheet );
402
		if ( empty( $post ) ) {
403
			return $post;
404
		}
405
		return (bool) wp_get_post_revisions( $post );
406
	}
407
408
	/**
409
	 * Core doesn't have a function to get the revisions url for a given post ID.
410
	 *
411
	 * @param string $stylesheet Stylesheet name.
412
	 *
413
	 * @return null|string|void
414
	 */
415
	public static function get_revisions_url( $stylesheet = '' ) {
416
		$post = wp_get_custom_css_post( $stylesheet );
417
418
		// If we have any currently saved customizations...
419
		if ( $post instanceof WP_Post ) {
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
420
			$revisions = wp_get_post_revisions( $post->ID, array( 'posts_per_page' => 1 ) );
421
			if ( empty( $revisions ) || is_wp_error( $revisions ) ) {
422
				return admin_url( 'themes.php?page=editcss' );
423
			}
424
			$revision = reset( $revisions );
425
			return get_edit_post_link( $revision->ID );
426
		}
427
428
		return admin_url( 'themes.php?page=editcss' );
429
	}
430
431
	/**
432
	 * Get a map of all theme names and theme stylesheets for mapping stuff.
433
	 *
434
	 * @return array
435
	 */
436
	public static function get_themes() {
437
		$themes = wp_get_themes( array( 'errors' => null ) );
438
		$all = array();
439
		foreach ( $themes as $theme ) {
440
			$all[ $theme->name ] = $theme->stylesheet;
441
		}
442
		return $all;
443
	}
444
445
	/**
446
	 * When we need to get all themes that have Custom CSS saved.
447
	 *
448
	 * @return array
449
	 */
450
	public static function get_all_themes_with_custom_css() {
451
		$themes = self::get_themes();
452
		$custom_css = get_posts( array(
453
			'post_type'   => 'custom_css',
454
			'post_status' => get_post_stati(),
455
			'number'      => -1,
456
			'order'       => 'DESC',
457
			'orderby'     => 'modified',
458
		) );
459
		$return = array();
460
461
		foreach ( $custom_css as $post ) {
462
			$stylesheet = $post->post_title;
463
			$label      = array_search( $stylesheet, $themes );
464
465
			if ( ! $label ) {
466
				continue;
467
			}
468
469
			$return[ $stylesheet ] = array(
470
				'label' => $label,
471
				'post'  => $post,
472
			);
473
		}
474
475
		return $return;
476
	}
477
478
	/**
479
	 * Handle the enqueueing of scripts for customize previews.
480
	 */
481
	public static function wp_enqueue_scripts() {
482
		if ( is_customize_preview() ) {
483
			wp_enqueue_script( 'jetpack-customizer-css-preview' );
484
			wp_localize_script( 'jetpack-customizer-css-preview', 'jpCustomizerCssPreview', array(
485
				/** This filter is documented in modules/custom-css/custom-css.php */
486
				'preprocessors' => apply_filters( 'jetpack_custom_css_preprocessors', array() ),
487
			));
488
		}
489
	}
490
491
	/**
492
	 * Sanitize the CSS for users without `unfiltered_html`.
493
	 *
494
	 * @param string $css  Input CSS.
495
	 * @param array  $args Array of CSS options.
496
	 *
497
	 * @return mixed|string
498
	 */
499
	public static function sanitize_css( $css, $args = array() ) {
500
		$args = wp_parse_args( $args, array(
0 ignored issues
show
Documentation introduced by
array('force' => false, 'preprocessor' => null) is of type array<string,false|null,..."preprocessor":"null"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
501
			'force'        => false,
502
			'preprocessor' => null,
503
		) );
504
505
		if ( $args['force'] || ! current_user_can( 'unfiltered_html' ) ) {
506
507
			$warnings = array();
508
509
			safecss_class();
510
			$csstidy = new csstidy();
511
			$csstidy->optimise = new safecss( $csstidy );
0 ignored issues
show
Documentation introduced by
$csstidy is of type object<csstidy>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
512
513
			$csstidy->set_cfg( 'remove_bslash',              false );
514
			$csstidy->set_cfg( 'compress_colors',            false );
515
			$csstidy->set_cfg( 'compress_font-weight',       false );
516
			$csstidy->set_cfg( 'optimise_shorthands',        0 );
517
			$csstidy->set_cfg( 'remove_last_;',              false );
518
			$csstidy->set_cfg( 'case_properties',            false );
519
			$csstidy->set_cfg( 'discard_invalid_properties', true );
520
			$csstidy->set_cfg( 'css_level',                  'CSS3.0' );
521
			$csstidy->set_cfg( 'preserve_css',               true );
522
			$csstidy->set_cfg( 'template',                   dirname( __FILE__ ) . '/csstidy/wordpress-standard.tpl' );
523
524
			// Test for some preg_replace stuff.
525
			{
526
				$prev = $css;
527
				$css = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $css );
528
				// prevent content: '\3434' from turning into '\\3434'.
529
				$css = str_replace( array( '\'\\\\', '"\\\\' ), array( '\'\\', '"\\' ), $css );
530
				if ( $css !== $prev ) {
531
					$warnings[] = 'preg_replace found stuff';
532
				}
533
			}
534
535
			// Some people put weird stuff in their CSS, KSES tends to be greedy.
536
			$css = str_replace( '<=', '&lt;=', $css );
537
538
			// Test for some kses stuff.
539
			{
540
				$prev = $css;
541
				// Why KSES instead of strip_tags?  Who knows?
542
				$css = wp_kses_split( $css, array(), array() );
543
				$css = str_replace( '&gt;', '>', $css ); // kses replaces lone '>' with &gt;
544
				// Why both KSES and strip_tags?  Because we just added some '>'.
545
				$css = strip_tags( $css );
546
547
				if ( $css != $prev ) {
548
					$warnings[] = 'kses found stuff';
549
				}
550
			}
551
552
			// if we're not using a preprocessor.
553 View Code Duplication
			if ( ! $args['preprocessor'] ) {
554
555
				/** This action is documented in modules/custom-css/custom-css.php */
556
				do_action( 'safecss_parse_pre', $csstidy, $css, $args );
557
558
				$csstidy->parse( $css );
559
560
				/** This action is documented in modules/custom-css/custom-css.php */
561
				do_action( 'safecss_parse_post', $csstidy, $warnings, $args );
562
563
				$css = $csstidy->print->plain();
564
			}
565
		}
566
		return $css;
567
	}
568
569
	/**
570
	 * Override $content_width in customizer previews.
571
	 */
572
	public static function preview_content_width() {
573
		global $wp_customize;
574
		if ( ! is_customize_preview() ) {
575
			return;
576
		}
577
578
		$setting = $wp_customize->get_setting( 'jetpack_custom_css[content_width]' );
579
		if ( ! $setting ) {
580
			return;
581
		}
582
583
		$customized_content_width = (int) $setting->post_value();
584
		if ( ! empty( $customized_content_width ) ) {
585
			$GLOBALS['content_width'] = $customized_content_width;
586
		}
587
	}
588
589
	/**
590
	 * Filter the current theme's stylesheet for potentially nullifying it.
591
	 *
592
	 * @param string $current Stylesheet URI for the current theme/child theme.
593
	 *
594
	 * @return mixed|void
595
	 */
596
	static function style_filter( $current ) {
597
		if ( is_admin() ) {
598
			return $current;
599
		} elseif ( self::is_freetrial() && ( ! self::is_preview() || ! current_user_can( 'switch_themes' ) ) ) {
600
			return $current;
601
		} elseif ( self::skip_stylesheet() ) {
602
			/** This filter is documented in modules/custom-css/custom-css.php */
603
			return apply_filters( 'safecss_style_filter_url', plugins_url( 'custom-css/css/blank.css', __FILE__ ) );
604
		}
605
606
		return $current;
607
	}
608
609
	/**
610
	 * Determine whether or not we should have the theme skip its main stylesheet.
611
	 *
612
	 * @return mixed The truthiness of this value determines whether the stylesheet should be skipped.
613
	 */
614
	static function skip_stylesheet() {
615
		/** This filter is documented in modules/custom-css/custom-css.php */
616
		$skip_stylesheet = apply_filters( 'safecss_skip_stylesheet', null );
617
		if ( ! is_null( $skip_stylesheet ) ) {
618
			return $skip_stylesheet;
619
		}
620
621
		$jetpack_custom_css = get_theme_mod( 'jetpack_custom_css', array() );
622
		if ( isset( $jetpack_custom_css['replace'] ) ) {
623
			return $jetpack_custom_css['replace'];
624
		}
625
626
		return false;
627
	}
628
629
	/**
630
	 * Override $content_width in customizer previews.
631
	 *
632
	 * Runs on `safecss_skip_stylesheet` filter.
633
	 *
634
	 * @param bool $skip_value Should the stylesheet be skipped.
635
	 *
636
	 * @return null|bool
637
	 */
638
	public static function preview_skip_stylesheet( $skip_value ) {
639
		global $wp_customize;
640
		if ( ! is_customize_preview() ) {
641
			return $skip_value;
642
		}
643
644
		$setting = $wp_customize->get_setting( 'jetpack_custom_css[replace]' );
645
		if ( ! $setting ) {
646
			return $skip_value;
647
		}
648
649
		$customized_replace = $setting->post_value();
650
		if ( null !== $customized_replace ) {
651
			return $customized_replace;
652
		}
653
654
		return $skip_value;
655
	}
656
657
	/**
658
	 * Add Custom CSS section and controls.
659
	 *
660
	 * @param WP_Customize_Manager $wp_customize WP_Customize_Manager instance.
661
	 */
662
	public static function customize_register( $wp_customize ) {
663
664
		/**
665
		 * SETTINGS.
666
		 */
667
668
		$wp_customize->add_setting( 'jetpack_custom_css[preprocessor]', array(
669
			'default' => '',
670
			'transport' => 'postMessage',
671
			'sanitize_callback' => array( __CLASS__, 'sanitize_preprocessor' ),
672
		) );
673
674
		$wp_customize->add_setting( 'jetpack_custom_css[replace]', array(
675
			'default' => false,
676
			'transport' => 'refresh',
677
		) );
678
679
		$wp_customize->add_setting( 'jetpack_custom_css[content_width]', array(
680
			'default' => '',
681
			'transport' => 'refresh',
682
			'sanitize_callback' => array( __CLASS__, 'intval_base10' ),
683
		) );
684
685
		// Add custom sanitization to the core css customizer setting.
686
		foreach ( $wp_customize->settings() as $setting ) {
687
			if ( $setting instanceof WP_Customize_Custom_CSS_Setting ) {
0 ignored issues
show
Bug introduced by
The class WP_Customize_Custom_CSS_Setting does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
688
				add_filter( "customize_sanitize_{$setting->id}", array( __CLASS__, 'sanitize_css_callback' ), 10, 2 );
689
			}
690
		}
691
692
		/**
693
		 * CONTROLS.
694
		 */
695
696
		// Overwrite or Tweak the Core Control.
697
		$core_custom_css = $wp_customize->get_control( 'custom_css' );
698
		if ( $core_custom_css ) {
699
			if ( $core_custom_css instanceof WP_Customize_Code_Editor_Control ) {
0 ignored issues
show
Bug introduced by
The class WP_Customize_Code_Editor_Control does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
700
				// In WP 4.9, we let the Core CodeMirror control keep running the show, but hook into it to tweak stuff.
701
				$types = array(
702
					'default' => 'text/css',
703
					'less'    => 'text/x-less',
704
					'sass'    => 'text/x-scss',
705
				);
706
				$preprocessor = $wp_customize->get_setting( 'jetpack_custom_css[preprocessor]' )->value();
707
				if ( isset( $types[ $preprocessor ] ) ) {
708
					$core_custom_css->code_type = $types[ $preprocessor ];
709
				}
710
			} else {
711
				// Core < 4.9 Fallback
712
				$core_custom_css->type = 'jetpackCss';
713
			}
714
		}
715
716
		$wp_customize->selective_refresh->add_partial( 'custom_css', array(
717
			'type'                => 'custom_css',
718
			'selector'            => '#wp-custom-css',
719
			'container_inclusive' => false,
720
			'fallback_refresh'    => false,
721
			'settings'            => array(
722
				'custom_css[' . $wp_customize->get_stylesheet() . ']',
723
				'jetpack_custom_css[preprocessor]',
724
			),
725
			'render_callback' => array( __CLASS__, 'echo_custom_css_partial' ),
726
		) );
727
728
		$wp_customize->add_control( 'wpcom_custom_css_content_width_control', array(
729
			'type'     => 'text',
730
			'label'    => __( 'Media Width', 'jetpack' ),
731
			'section'  => 'custom_css',
732
			'settings' => 'jetpack_custom_css[content_width]',
733
		) );
734
735
		$wp_customize->add_control( 'jetpack_css_mode_control', array(
736
			'type'     => 'checkbox',
737
			'label'    => __( 'Don\'t use the theme\'s original CSS.', 'jetpack' ),
738
			'section'  => 'custom_css',
739
			'settings' => 'jetpack_custom_css[replace]',
740
		) );
741
742
		/**
743
		 * An action to grab on to if another Jetpack Module would like to add its own controls.
744
		 *
745
		 * @module custom-css
746
		 *
747
		 * @since 4.4.2
748
		 *
749
		 * @param $wp_customize The WP_Customize object.
750
		 */
751
		do_action( 'jetpack_custom_css_customizer_controls', $wp_customize );
752
753
		/** This filter is documented in modules/custom-css/custom-css.php */
754
		$preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
755
		if ( ! empty( $preprocessors ) ) {
756
			$preprocessor_choices = array(
757
				'' => __( 'None', 'jetpack' ),
758
			);
759
760
			foreach ( $preprocessors as $preprocessor_key => $processor ) {
761
				$preprocessor_choices[ $preprocessor_key ] = $processor['name'];
762
			}
763
764
			$wp_customize->add_control( 'jetpack_css_preprocessors_control', array(
765
				'type'     => 'select',
766
				'choices'  => $preprocessor_choices,
767
				'label'    => __( 'Preprocessor', 'jetpack' ),
768
				'section'  => 'custom_css',
769
				'settings' => 'jetpack_custom_css[preprocessor]',
770
			) );
771
		}
772
773
	}
774
775
	/**
776
	 * The callback to handle sanitizing the CSS. Takes different arguments, hence the proxy function.
777
	 *
778
	 * @param mixed                $css     Value of the setting.
779
	 * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
780
	 *
781
	 * @return mixed|string
782
	 */
783
	public static function sanitize_css_callback( $css, $setting ) {
784
		global $wp_customize;
785
		return self::sanitize_css( $css, array(
786
			'preprocessor' => $wp_customize->get_setting( 'jetpack_custom_css[preprocessor]' )->value(),
787
		) );
788
	}
789
790
	/**
791
	 * Flesh out for wpcom.
792
	 *
793
	 * @todo
794
	 *
795
	 * @return bool
796
	 */
797
	public static function is_freetrial() {
798
		return false;
799
	}
800
801
	/**
802
	 * Flesh out for wpcom.
803
	 *
804
	 * @todo
805
	 *
806
	 * @return bool
807
	 */
808
	public static function is_preview() {
809
		return false;
810
	}
811
812
	/**
813
	 * Output the custom css for customize preview.
814
	 *
815
	 * @param string $css Custom CSS content.
816
	 *
817
	 * @return mixed
818
	 */
819
	public static function customize_preview_wp_get_custom_css( $css ) {
820
		global $wp_customize;
821
822
		$preprocessor = $wp_customize->get_setting( 'jetpack_custom_css[preprocessor]' )->value();
823
824
		// If it's empty, just return.
825
		if ( empty( $preprocessor ) ) {
826
			return $css;
827
		}
828
829
		/** This filter is documented in modules/custom-css/custom-css.php */
830
		$preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
831
		if ( isset( $preprocessors[ $preprocessor ] ) ) {
832
			return call_user_func( $preprocessors[ $preprocessor ]['callback'], $css );
833
		}
834
835
		return $css;
836
	}
837
838
	/**
839
	 * Add CSS preprocessing to our CSS if it is supported.
840
	 *
841
	 * @param mixed                $css     Value of the setting.
842
	 * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
843
	 *
844
	 * @return string
845
	 */
846
	public static function customize_value_custom_css( $css, $setting ) {
847
		// Find the current preprocessor.
848
		$jetpack_custom_css = get_theme_mod( 'jetpack_custom_css', array() );
849
		if ( isset( $jetpack_custom_css['preprocessor'] ) ) {
850
			$preprocessor = $jetpack_custom_css['preprocessor'];
851
		}
852
853
		// If it's not supported, just return.
854
		/** This filter is documented in modules/custom-css/custom-css.php */
855
		$preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
856
		if ( ! isset( $preprocessors[ $preprocessor ] ) ) {
857
			return $css;
858
		}
859
860
		// Swap it for the `post_content_filtered` instead.
861
		$post = wp_get_custom_css_post( $setting->stylesheet );
862
		if ( $post && ! empty( $post->post_content_filtered ) ) {
863
			$css = $post->post_content_filtered;
864
		}
865
866
		return $css;
867
	}
868
869
	/**
870
	 * Store the original pre-processed CSS in `post_content_filtered`
871
	 * and then store processed CSS in `post_content`.
872
	 *
873
	 * @param array                           $args    Content post args.
874
	 * @param string                          $css     Original CSS being updated.
875
	 * @param WP_Customize_Custom_CSS_Setting $setting Custom CSS Setting.
876
	 *
877
	 * @return mixed
878
	 */
879
	public static function customize_update_custom_css_post_content_args( $args, $css, $setting ) {
880
		// Find the current preprocessor.
881
		$jetpack_custom_css = get_theme_mod( 'jetpack_custom_css', array() );
882
		if ( empty( $jetpack_custom_css['preprocessor'] ) ) {
883
			return $args;
884
		}
885
886
		$preprocessor = $jetpack_custom_css['preprocessor'];
887
		/** This filter is documented in modules/custom-css/custom-css.php */
888
		$preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
889
890
		// If it's empty, just return.
891
		if ( empty( $preprocessor ) ) {
892
			return $args;
893
		}
894
895 View Code Duplication
		if ( isset( $preprocessors[ $preprocessor ] ) ) {
896
			$args['post_content_filtered'] = $css;
897
			$args['post_content'] = call_user_func( $preprocessors[ $preprocessor ]['callback'], $css );
898
		}
899
900
		return $args;
901
	}
902
903
	/**
904
	 * Filter to handle the processing of preprocessed css on save.
905
	 *
906
	 * @param array  $args       Custom CSS options.
907
	 * @param string $stylesheet Original CSS to be updated.
908
	 *
909
	 * @return mixed
910
	 */
911
	public static function update_custom_css_data( $args, $stylesheet ) {
912
		// Find the current preprocessor.
913
		$jetpack_custom_css = get_theme_mod( 'jetpack_custom_css', array() );
914
		if ( empty( $jetpack_custom_css['preprocessor'] ) ) {
915
			return $args;
916
		}
917
918
		/** This filter is documented in modules/custom-css/custom-css.php */
919
		$preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
920
		$preprocessor = $jetpack_custom_css['preprocessor'];
921
922
		// If we have a preprocessor specified ...
923
		if ( isset( $preprocessors[ $preprocessor ] ) ) {
924
			// And no other preprocessor has run ...
925
			if ( empty( $args['preprocessed'] ) ) {
926
				$args['preprocessed'] = $args['css'];
927
				$args['css'] = call_user_func( $preprocessors[ $preprocessor ]['callback'], $args['css'] );
928
			} else {
929
				trigger_error( 'Jetpack CSS Preprocessor specified, but something else has already modified the argument.', E_USER_WARNING );
930
			}
931
		}
932
933
		return $args;
934
	}
935
936
	/**
937
	 * When on the edit screen, make sure the custom content width
938
	 * setting is applied to the large image size.
939
	 *
940
	 * @param array  $dims    Array of image dimensions (width and height).
941
	 * @param string $size    Size of the resulting image.
942
	 * @param null   $context Context the image is being resized for. `edit` or `display`.
943
	 *
944
	 * @return array
945
	 */
946 View Code Duplication
	static function editor_max_image_size( $dims, $size = 'medium', $context = null ) {
947
		list( $width, $height ) = $dims;
948
949
		if ( 'large' === $size && 'edit' === $context ) {
950
			$width = Jetpack::get_content_width();
951
		}
952
953
		return array( $width, $height );
954
	}
955
956
	/**
957
	 * Override the content_width with a custom value if one is set.
958
	 *
959
	 * @param int $content_width Content Width value to be updated.
960
	 *
961
	 * @return int
962
	 */
963
	static function jetpack_content_width( $content_width ) {
964
		$custom_content_width = 0;
965
966
		$jetpack_custom_css = get_theme_mod( 'jetpack_custom_css', array() );
967
		if ( isset( $jetpack_custom_css['content_width'] ) ) {
968
			$custom_content_width = $jetpack_custom_css['content_width'];
969
		}
970
971
		if ( $custom_content_width > 0 ) {
972
			return $custom_content_width;
973
		}
974
975
		return $content_width;
976
	}
977
978
	/**
979
	 * Currently this filter function gets called on
980
	 * 'template_redirect' action and
981
	 * 'admin_init' action
982
	 */
983 View Code Duplication
	static function set_content_width() {
984
		// Don't apply this filter on the Edit CSS page.
985
		if ( isset( $_GET['page'] ) && 'editcss' === $_GET['page'] && is_admin() ) {
986
			return;
987
		}
988
989
		$GLOBALS['content_width'] = Jetpack::get_content_width();
990
	}
991
992
	/**
993
	 * Make sure the preprocessor we're saving is one we know about.
994
	 *
995
	 * @param string $preprocessor The preprocessor to sanitize.
996
	 *
997
	 * @return null|string
998
	 */
999
	public static function sanitize_preprocessor( $preprocessor ) {
1000
		/** This filter is documented in modules/custom-css/custom-css.php */
1001
		$preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
1002
		if ( empty( $preprocessor ) || array_key_exists( $preprocessor, $preprocessors ) ) {
1003
			return $preprocessor;
1004
		}
1005
		return null;
1006
	}
1007
1008
	/**
1009
	 * Get the base10 intval.
1010
	 *
1011
	 * This is used as a setting's sanitize_callback; we can't use just plain
1012
	 * intval because the second argument is not what intval() expects.
1013
	 *
1014
	 * @access public
1015
	 *
1016
	 * @param mixed $value Number to convert.
1017
	 * @return int Integer.
1018
	 */
1019
	public static function intval_base10( $value ) {
1020
		return intval( $value, 10 );
1021
	}
1022
1023
	/**
1024
	 * Add a footer action on revision.php to print some customizations for the theme switcher.
1025
	 */
1026
	public static function load_revision_php() {
1027
		add_action( 'admin_footer', array( __CLASS__, 'revision_admin_footer' ) );
1028
	}
1029
1030
	/**
1031
	 * Print the theme switcher on revision.php and move it into place.
1032
	 */
1033
	public static function revision_admin_footer() {
1034
		$post = get_post();
1035
		if ( 'custom_css' !== $post->post_type ) {
1036
			return;
1037
		}
1038
		$stylesheet = $post->post_title;
1039
		?>
1040
<script type="text/html" id="tmpl-other-themes-switcher">
1041
	<?php self::revisions_switcher_box( $stylesheet ); ?>
1042
</script>
1043
<style>
1044
.other-themes-wrap {
1045
	float: right;
1046
	background-color: #fff;
1047
	-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.1);
1048
	box-shadow: 0 1px 3px rgba(0,0,0,0.1);
1049
	padding: 5px 10px;
1050
	margin-bottom: 10px;
1051
}
1052
.other-themes-wrap label {
1053
	display: block;
1054
	margin-bottom: 10px;
1055
}
1056
.other-themes-wrap select {
1057
	float: left;
1058
	width: 77%;
1059
}
1060
.other-themes-wrap button {
1061
	float: right;
1062
	width: 20%;
1063
}
1064
.revisions {
1065
	clear: both;
1066
}
1067
/* Hide the back-to-post link */
1068
.long-header + a {
1069
	display: none;
1070
}
1071
</style>
1072
<script>
1073
(function($){
1074
	var switcher = $('#tmpl-other-themes-switcher').html(),
1075
		qty = $( switcher ).find('select option').length,
1076
		$switcher;
1077
1078
	if ( qty >= 3 ) {
1079
		$('h1.long-header').before( switcher );
1080
		$switcher = $('.other-themes-wrap');
1081
		$switcher.find('button').on('click', function(e){
1082
			e.preventDefault();
1083
			if ( $switcher.find('select').val() ) {
1084
				window.location.href = $switcher.find('select').val();
1085
			}
1086
		})
1087
	}
1088
})(jQuery);
1089
</script>
1090
		<?php
1091
	}
1092
1093
	/**
1094
	 * The HTML for the theme revision switcher box.
1095
	 *
1096
	 * @param string $stylesheet Stylesheet name.
1097
	 */
1098
	public static function revisions_switcher_box( $stylesheet = '' ) {
1099
		$themes = self::get_all_themes_with_custom_css();
1100
		?>
1101
		<div class="other-themes-wrap">
1102
			<label for="other-themes"><?php esc_html_e( 'Select another theme to view its custom CSS.', 'jetpack' ); ?></label>
1103
			<select id="other-themes">
1104
				<option value=""><?php esc_html_e( 'Select a theme&hellip;', 'jetpack' ); ?></option>
1105
				<?php
1106
				foreach ( $themes as $theme_stylesheet => $data ) {
1107
					$revisions = wp_get_post_revisions( $data['post']->ID, array( 'posts_per_page' => 1 ) );
1108
					if ( ! $revisions ) {
1109
						?>
1110
						<option value="<?php echo esc_url( add_query_arg( 'id', $data['post']->ID, menu_page_url( 'editcss', 0 ) ) ); ?>" <?php disabled( $stylesheet, $theme_stylesheet ); ?>>
1111
							<?php echo esc_html( $data['label'] ); ?>
1112
							<?php printf( esc_html__( '(modified %s ago)', 'jetpack' ), human_time_diff( strtotime( $data['post']->post_modified_gmt ) ) ); ?></option>
1113
						<?php
1114
						continue;
1115
					}
1116
					$revision = array_shift( $revisions );
1117
					?>
1118
					<option value="<?php echo esc_url( get_edit_post_link( $revision->ID ) ); ?>" <?php disabled( $stylesheet, $theme_stylesheet ); ?>>
1119
						<?php echo esc_html( $data['label'] ); ?>
1120
						<?php printf( esc_html__( '(modified %s ago)', 'jetpack' ), human_time_diff( strtotime( $data['post']->post_modified_gmt ) ) ); ?></option>
1121
					<?php
1122
				}
1123
				?>
1124
			</select>
1125
			<button class="button" id="other_theme_custom_css_switcher"><?php esc_html_e( 'Switch', 'jetpack' ); ?></button>
1126
		</div>
1127
		<?php
1128
	}
1129
}
1130
1131
Jetpack_Custom_CSS_Enhancements::add_hooks();
1132
1133 View Code Duplication
if ( ! function_exists( 'safecss_class' ) ) :
1134
	/**
1135
	 * Load in the class only when needed.  Makes lighter load by having one less class in memory.
1136
	 */
1137
	function safecss_class() {
1138
		// Wrapped so we don't need the parent class just to load the plugin.
1139
		if ( class_exists( 'safecss' ) ) {
1140
			return;
1141
		}
1142
1143
		require_once( dirname( __FILE__ ) . '/csstidy/class.csstidy.php' );
1144
1145
		/**
1146
		 * Class safecss
1147
		 */
1148
		class safecss extends csstidy_optimise {
1149
1150
			/**
1151
			 * Optimises $css after parsing.
1152
			 */
1153
			function postparse() {
1154
1155
				/** This action is documented in modules/custom-css/custom-css.php */
1156
				do_action( 'csstidy_optimize_postparse', $this );
1157
1158
				return parent::postparse();
1159
			}
1160
1161
			/**
1162
			 * Optimises a sub-value.
1163
			 */
1164
			function subvalue() {
1165
1166
				/** This action is documented in modules/custom-css/custom-css.php */
1167
				do_action( 'csstidy_optimize_subvalue', $this );
1168
1169
				return parent::subvalue();
1170
			}
1171
		}
1172
	}
1173
endif;
1174