true, // 'public' => false, // 'publicly_queryable' => false, // 'show_ui' => false, 'supports' => array( 'revisions' ), 'label' => 'Custom CSS', 'can_export' => false, 'rewrite' => false, 'capabilities' => array( 'edit_post' => 'edit_theme_options', 'read_post' => 'read', 'delete_post' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', 'edit_others_posts' => 'edit_theme_options', 'publish_posts' => 'edit_theme_options', 'read_private_posts' => 'read' ) ) ); // Short-circuit WP if this is a CSS stylesheet request if ( isset( $_GET['custom-css'] ) ) { header( 'Content-Type: text/css', true, 200 ); header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 31536000) . ' GMT' ); // 1 year Jetpack_Custom_CSS::print_css(); exit; } add_action( 'admin_enqueue_scripts', array( 'Jetpack_Custom_CSS', 'enqueue_scripts' ) ); if ( isset( $_GET['page'] ) && 'editcss' == $_GET['page'] && is_admin() ) { // Do migration routine if necessary Jetpack_Custom_CSS::upgrade(); /** * Allows additional work when migrating safecss from wp_options to wp_post. * * @module custom-css * * @since 1.7.0 */ do_action( 'safecss_migrate_post' ); } /** * Never embed the style in the head on wpcom. * Yes, this filter should be added to an unsynced file on wpcom, but * there is no good syntactically-correct location to put it yet. * @link https://github.com/Automattic/jetpack/commit/a1be114e9179f64d147124727a58e2cf76c7e5a1#commitcomment-7763921 */ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { add_filter( 'safecss_embed_style', '__return_false' ); } else { add_filter( 'safecss_embed_style', array( 'Jetpack_Custom_CSS', 'should_we_inline_custom_css' ), 10, 2 ); } add_action( 'wp_head', array( 'Jetpack_Custom_CSS', 'link_tag' ), 101 ); add_filter( 'jetpack_content_width', array( 'Jetpack_Custom_CSS', 'jetpack_content_width' ) ); add_filter( 'editor_max_image_size', array( 'Jetpack_Custom_CSS', 'editor_max_image_size' ), 10, 3 ); if ( !current_user_can( 'switch_themes' ) && !is_super_admin() ) return; add_action( 'admin_menu', array( 'Jetpack_Custom_CSS', 'menu' ) ); if ( isset( $_POST['safecss'] ) && false == strstr( $_SERVER[ 'REQUEST_URI' ], 'options.php' ) ) { check_admin_referer( 'safecss' ); $save_result = self::save( array( 'css' => stripslashes( $_POST['safecss'] ), 'is_preview' => isset( $_POST['action'] ) && $_POST['action'] == 'preview', 'preprocessor' => isset( $_POST['custom_css_preprocessor'] ) ? $_POST['custom_css_preprocessor'] : '', 'add_to_existing' => isset( $_POST['add_to_existing'] ) ? $_POST['add_to_existing'] == 'true' : true, 'content_width' => isset( $_POST['custom_content_width'] ) ? $_POST['custom_content_width'] : false, ) ); if ( $_POST['action'] == 'preview' ) { wp_safe_redirect( add_query_arg( 'csspreview', 'true', get_option( 'home' ) ) ); exit; } if ( $save_result ) add_action( 'admin_notices', array( 'Jetpack_Custom_CSS', 'saved_message' ) ); } // Prevent content filters running on CSS when restoring revisions if ( isset( $_REQUEST[ 'action' ] ) && 'restore' === $_REQUEST[ 'action' ] && false !== strstr( $_SERVER[ 'REQUEST_URI' ], 'revision.php' ) ) { $parent_post = get_post( wp_get_post_parent_id( intval( $_REQUEST[ 'revision' ] ) ) ); if ( $parent_post && ! is_wp_error( $parent_post ) && 'safecss' === $parent_post->post_type ) { // Remove wp_filter_post_kses, this causes CSS escaping issues remove_filter( 'content_save_pre', 'wp_filter_post_kses' ); remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); remove_all_filters( 'content_save_pre' ); } } // Modify all internal links so that preview state persists if ( Jetpack_Custom_CSS::is_preview() ) ob_start( array( 'Jetpack_Custom_CSS', 'buffer' ) ); } /** * Save new custom CSS. This should be the entry point for any third-party code using Jetpack_Custom_CSS * to save CSS. * * @param array $args Array of arguments: * string $css The CSS (or LESS or Sass) * bool $is_preview Whether this CSS is preview or published * string preprocessor Which CSS preprocessor to use * bool $add_to_existing Whether this CSS replaces the theme's CSS or supplements it. * int $content_width A custom $content_width to go along with this CSS. * @return int The post ID of the saved Custom CSS post. */ public static function save( $args = array() ) { $defaults = array( 'css' => '', 'is_preview' => false, 'preprocessor' => '', 'add_to_existing' => true, 'content_width' => false, ); $args = wp_parse_args( $args, $defaults ); if ( $args['content_width'] && intval( $args['content_width']) > 0 && ( ! isset( $GLOBALS['content_width'] ) || $args['content_width'] != $GLOBALS['content_width'] ) ) $args['content_width'] = intval( $args['content_width'] ); else $args['content_width'] = false; // Remove wp_filter_post_kses, this causes CSS escaping issues remove_filter( 'content_save_pre', 'wp_filter_post_kses' ); remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); remove_all_filters( 'content_save_pre' ); /** * Fires prior to saving custom css values. Necessitated because the * core WordPress save_pre filters were removed: * - content_save_pre * - content_filtered_save_pre * * @module custom-css * * @since 1.7.0 * * @param array $args { * Array of custom CSS arguments. * @type string $css The CSS (or LESS or Sass). * @type bool $is_preview Whether this CSS is preview or published. * @type string preprocessor Which CSS preprocessor to use. * @type bool $add_to_existing Whether this CSS replaces the theme's CSS or supplements it. * @type int $content_width A custom $content_width to go along with this CSS. * } */ do_action( 'safecss_save_pre', $args ); $warnings = array(); safecss_class(); $csstidy = new csstidy(); $csstidy->optimise = new safecss( $csstidy ); $csstidy->set_cfg( 'remove_bslash', false ); $csstidy->set_cfg( 'compress_colors', false ); $csstidy->set_cfg( 'compress_font-weight', false ); $csstidy->set_cfg( 'optimise_shorthands', 0 ); $csstidy->set_cfg( 'remove_last_;', false ); $csstidy->set_cfg( 'case_properties', false ); $csstidy->set_cfg( 'discard_invalid_properties', true ); $csstidy->set_cfg( 'css_level', 'CSS3.0' ); $csstidy->set_cfg( 'preserve_css', true ); $csstidy->set_cfg( 'template', dirname( __FILE__ ) . '/csstidy/wordpress-standard.tpl' ); $css = $orig = $args['css']; $css = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $prev = $css ); // prevent content: '\3434' from turning into '\\3434' $css = str_replace( array( '\'\\\\', '"\\\\' ), array( '\'\\', '"\\' ), $css ); if ( $css != $prev ) $warnings[] = 'preg_replace found stuff'; // Some people put weird stuff in their CSS, KSES tends to be greedy $css = str_replace( '<=', '<=', $css ); // Why KSES instead of strip_tags? Who knows? $css = wp_kses_split( $prev = $css, array(), array() ); $css = str_replace( '>', '>', $css ); // kses replaces lone '>' with > // Why both KSES and strip_tags? Because we just added some '>'. $css = strip_tags( $css ); if ( $css != $prev ) $warnings[] = 'kses found stuff'; // if we're not using a preprocessor if ( ! $args['preprocessor'] ) { /** * Fires before parsing the css with CSSTidy, but only if * the preprocessor is not configured for use. * * @module custom-css * * @since 1.7.0 * * @param obj $csstidy The csstidy object. * @param string $css Custom CSS. * @param array $args Array of custom CSS arguments. */ do_action( 'safecss_parse_pre', $csstidy, $css, $args ); $csstidy->parse( $css ); /** * Fires after parsing the css with CSSTidy, but only if * the preprocessor is not cinfigured for use. * * @module custom-css * * @since 1.7.0 * * @param obj $csstidy The csstidy object. * @param array $warnings Array of warnings. * @param array $args Array of custom CSS arguments. */ do_action( 'safecss_parse_post', $csstidy, $warnings, $args ); $css = $csstidy->print->plain(); } if ( $args['add_to_existing'] ) $add_to_existing = 'yes'; else $add_to_existing = 'no'; if ( $args['is_preview'] || Jetpack_Custom_CSS::is_freetrial() ) { // Save the CSS $safecss_revision_id = Jetpack_Custom_CSS::save_revision( $css, true, $args['preprocessor'] ); // Cache Buster update_option( 'safecss_preview_rev', intval( get_option( 'safecss_preview_rev' ) ) + 1); update_metadata( 'post', $safecss_revision_id, 'custom_css_add', $add_to_existing ); update_metadata( 'post', $safecss_revision_id, 'content_width', $args['content_width'] ); update_metadata( 'post', $safecss_revision_id, 'custom_css_preprocessor', $args['preprocessor'] ); delete_option( 'safecss_add' ); delete_option( 'safecss_content_width' ); if ( $args['is_preview'] ) { return $safecss_revision_id; } /** * Fires after saving Custom CSS. * * @module custom-css * * @since 1.7.0 */ do_action( 'safecss_save_preview_post' ); } // Save the CSS $safecss_post_id = Jetpack_Custom_CSS::save_revision( $css, false, $args['preprocessor'] ); $safecss_post_revision = Jetpack_Custom_CSS::get_current_revision(); update_option( 'safecss_rev', intval( get_option( 'safecss_rev' ) ) + 1 ); update_post_meta( $safecss_post_id, 'custom_css_add', $add_to_existing ); update_post_meta( $safecss_post_id, 'content_width', $args['content_width'] ); update_post_meta( $safecss_post_id, 'custom_css_preprocessor', $args['preprocessor'] ); delete_option( 'safecss_add' ); delete_option( 'safecss_content_width' ); update_metadata( 'post', $safecss_post_revision['ID'], 'custom_css_add', $add_to_existing ); update_metadata( 'post', $safecss_post_revision['ID'], 'content_width', $args['content_width'] ); update_metadata( 'post', $safecss_post_revision['ID'], 'custom_css_preprocessor', $args['preprocessor'] ); delete_option( 'safecss_preview_add' ); return $safecss_post_id; } /** * Get the published custom CSS post. * * @return array */ static function get_post() { $custom_css_post_id = Jetpack_Custom_CSS::post_id(); if ( $custom_css_post_id ) return get_post( $custom_css_post_id, ARRAY_A ); return array(); } /** * Get the post ID of the published custom CSS post. * * @return int|bool The post ID if it exists; false otherwise. */ static function post_id() { /** * Filter the ID of the post where Custom CSS is stored, before the ID is retrieved. * * If the callback function returns a non-null value, then post_id() will immediately * return that value, instead of retrieving the normal post ID. * * @module custom-css * * @since 3.8.1 * * @param null null The ID to return instead of the normal ID. */ $custom_css_post_id = apply_filters( 'jetpack_custom_css_pre_post_id', null ); if ( ! is_null( $custom_css_post_id ) ) { return $custom_css_post_id; } $custom_css_post_id = wp_cache_get( 'custom_css_post_id' ); if ( false === $custom_css_post_id ) { $custom_css_posts = get_posts( array( 'posts_per_page' => 1, 'post_type' => 'safecss', 'post_status' => 'publish', 'orderby' => 'date', 'order' => 'DESC' ) ); if ( count( $custom_css_posts ) > 0 ) $custom_css_post_id = $custom_css_posts[0]->ID; else $custom_css_post_id = 0; // Save post_id=0 to note that no safecss post exists. wp_cache_set( 'custom_css_post_id', $custom_css_post_id ); } if ( ! $custom_css_post_id ) return false; return $custom_css_post_id; } /** * Get the current revision of the original safecss record * * @return object */ static function get_current_revision() { $safecss_post = Jetpack_Custom_CSS::get_post(); if ( empty( $safecss_post ) ) { return false; } $revisions = wp_get_post_revisions( $safecss_post['ID'], array( 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC' ) ); // Empty array if no revisions exist if ( empty( $revisions ) ) { // Return original post return $safecss_post; } else { // Return the first entry in $revisions, this will be the current revision $current_revision = get_object_vars( array_shift( $revisions ) ); return $current_revision; } } /** * Save new revision of CSS * Checks to see if content was modified before really saving * * @param string $css * @param bool $is_preview * @return bool|int If nothing was saved, returns false. If a post * or revision was saved, returns the post ID. */ static function save_revision( $css, $is_preview = false, $preprocessor = '' ) { $safecss_post = Jetpack_Custom_CSS::get_post(); $compressed_css = Jetpack_Custom_CSS::minify( $css, $preprocessor ); // If null, there was no original safecss record, so create one if ( null == $safecss_post ) { if ( ! $css ) return false; $post = array(); $post['post_content'] = wp_slash( $css ); $post['post_title'] = 'safecss'; $post['post_status'] = 'publish'; $post['post_type'] = 'safecss'; $post['post_content_filtered'] = wp_slash( $compressed_css ); // Set excerpt to current theme, for display in revisions list $current_theme = wp_get_theme(); $post['post_excerpt'] = $current_theme->Name; // Insert the CSS into wp_posts $post_id = wp_insert_post( $post ); wp_cache_set( 'custom_css_post_id', $post_id ); return $post_id; } // Update CSS in post array with new value passed to this function $safecss_post['post_content'] = $css; $safecss_post['post_content_filtered'] = $compressed_css; // Set excerpt to current theme, for display in revisions list $current_theme = wp_get_theme(); $safecss_post['post_excerpt'] = $current_theme->Name; // Don't carry over last revision's timestamps, otherwise revisions all have matching timestamps unset( $safecss_post['post_date'] ); unset( $safecss_post['post_date_gmt'] ); unset( $safecss_post['post_modified'] ); unset( $safecss_post['post_modified_gmt'] ); // Do not update post if we are only saving a preview if ( false === $is_preview ) { $safecss_post['post_content'] = wp_slash( $safecss_post['post_content'] ); $safecss_post['post_content_filtered'] = wp_slash( $safecss_post['post_content_filtered'] ); $post_id = wp_update_post( $safecss_post ); wp_cache_set( 'custom_css_post_id', $post_id ); return $post_id; } else if ( ! defined( 'DOING_MIGRATE' ) ) { return _wp_put_post_revision( $safecss_post ); } } static function skip_stylesheet() { /** * Prevent the Custom CSS stylesheet from being enqueued. * * @module custom-css * * @since 2.2.1 * * @param null Should the stylesheet be skipped. Default to null. Anything else will force the stylesheet to be skipped. */ $skip_stylesheet = apply_filters( 'safecss_skip_stylesheet', null ); if ( null !== $skip_stylesheet ) { return $skip_stylesheet; } elseif ( Jetpack_Custom_CSS::is_customizer_preview() ) { return false; } else { if ( Jetpack_Custom_CSS::is_preview() ) { $safecss_post = Jetpack_Custom_CSS::get_current_revision(); if ( $safecss_post ) return (bool) ( get_post_meta( $safecss_post['ID'], 'custom_css_add', true ) == 'no' ); else return (bool) ( get_option( 'safecss_preview_add' ) == 'no' ); } else { $custom_css_post_id = Jetpack_Custom_CSS::post_id(); if ( $custom_css_post_id ) { $custom_css_add = get_post_meta( $custom_css_post_id, 'custom_css_add', true ); // It is possible for the CSS to be stored in a post but for the safecss_add option // to have not been upgraded yet if the user hasn't opened their Custom CSS editor // since October 2012. if ( ! empty( $custom_css_add ) ) return (bool) ( $custom_css_add === 'no' ); } return (bool) ( Jetpack_Options::get_option_and_ensure_autoload( 'safecss_add', '' ) == 'no' ); } } } static function is_preview() { return isset( $_GET['csspreview'] ) && $_GET['csspreview'] === 'true'; } /** * Currently this filter function gets called on * 'template_redirect' action and * 'admin_init' action */ static function set_content_width(){ // Don't apply this filter on the Edit CSS page if ( isset( $_GET ) && isset( $_GET['page'] ) && 'editcss' == $_GET['page'] && is_admin() ) { return; } $GLOBALS['content_width'] = Jetpack::get_content_width(); } /* * False when the site has the Custom Design upgrade. * Used only on WordPress.com. */ static function is_freetrial() { /** * Determine if a WordPress.com site uses a Free trial of the Custom Design Upgrade. * Used only on WordPress.com. * * @module custom-css * * @since 1.7.0 * * @param bool false Does the site use a Free trial of the Custom Design Upgrade. Default to false. */ return apply_filters( 'safecss_is_freetrial', false ); } static function get_preprocessor_key() { $safecss_post = Jetpack_Custom_CSS::get_current_revision(); return get_post_meta( $safecss_post['ID'], 'custom_css_preprocessor', true ); } static function get_preprocessor() { /** This filter is documented in modules/custom-css/custom-css.php */ $preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() ); $selected_preprocessor_key = self::get_preprocessor_key(); $selected_preprocessor = isset( $preprocessors[ $selected_preprocessor_key ] ) ? $preprocessors[ $selected_preprocessor_key ] : null; return $selected_preprocessor; } static function get_css( $compressed = false ) { /** * Filter the Custom CSS returned. * Can be used to return an error, or no CSS at all. * * @module custom-css * * @since 1.7.0 * * @param bool false Should we return an error instead of the Custom CSS. Default to false. */ $default_css = apply_filters( 'safecss_get_css_error', false ); if ( $default_css !== false ) return $default_css; $option = ( Jetpack_Custom_CSS::is_preview() || Jetpack_Custom_CSS::is_freetrial() ) ? 'safecss_preview' : 'safecss'; $css = ''; if ( 'safecss' == $option ) { // Don't bother checking for a migrated 'safecss' option if it never existed. if ( false === get_option( 'safecss' ) || get_option( 'safecss_revision_migrated' ) ) { $safecss_post = Jetpack_Custom_CSS::get_post(); if ( ! empty( $safecss_post ) ) { $css = ( $compressed && $safecss_post['post_content_filtered'] ) ? $safecss_post['post_content_filtered'] : $safecss_post['post_content']; } } else { $current_revision = Jetpack_Custom_CSS::get_current_revision(); if ( false === $current_revision ) { $css = ''; } else { $css = ( $compressed && $current_revision['post_content_filtered'] ) ? $current_revision['post_content_filtered'] : $current_revision['post_content']; } } // Fix for un-migrated Custom CSS if ( empty( $safecss_post ) ) { $_css = get_option( 'safecss' ); if ( !empty( $_css ) ) { $css = $_css; } } } else if ( 'safecss_preview' == $option ) { $safecss_post = Jetpack_Custom_CSS::get_current_revision(); $css = $safecss_post['post_content']; $css = Jetpack_Custom_CSS::minify( $css, get_post_meta( $safecss_post['ID'], 'custom_css_preprocessor', true ) ); } $css = str_replace( array( '\\\00BB \\\0020', '\0BB \020', '0BB 020' ), '\00BB \0020', $css ); if ( empty( $css ) ) { $css = "/*\n" . wordwrap( /** * Filter the default message displayed in the Custom CSS editor. * * @module custom-css * * @since 1.7.0 * * @param string $str Default Custom CSS editor content. */ apply_filters( 'safecss_default_css', __( "Welcome to Custom CSS!\n\nTo learn how this works, see http://wp.me/PEmnE-Bt", 'jetpack' ) ) ) . "\n*/"; } /** * Filter the Custom CSS returned from the editor. * * @module custom-css * * @since 1.7.0 * * @param string $css Custom CSS. */ $css = apply_filters( 'safecss_css', $css ); return $css; } static function replace_insecure_urls( $css ) { if ( ! function_exists( '_sa_get_frontend_https_url_replacement_map' ) ) { return $css; } list( $http_urls, $secure_urls ) = _sa_get_frontend_https_url_replacement_map(); return str_replace( $http_urls, $secure_urls, $css ); } static function print_css() { /** * Fires right before printing the custom CSS inside the element. * * @module custom-css * * @since 1.7.0 */ do_action( 'safecss_print_pre' ); $css = Jetpack_Custom_CSS::get_css( true ); echo self::replace_insecure_urls( $css ); } static function should_we_inline_custom_css( $should_we, $css ) { // If the CSS is less than 2,000 characters, inline it! otherwise return what was passed in. return ( strlen( $css ) < 2000 ) ? true : $should_we; } static function link_tag() { global $blog_id, $current_blog; if ( /** * Do not include any CSS on the page if the CSS includes an error. * Setting this filter to true stops any Custom CSS from being enqueued. * * @module custom-css * * @since 1.7.0 * * @param bool false Does the CSS include an error. Default to false. */ apply_filters( 'safecss_style_error', false ) ) { return; } if ( ! is_super_admin() && isset( $current_blog ) && ( 1 == $current_blog->spam || 1 == $current_blog->deleted ) ) return; if ( Jetpack_Custom_CSS::is_customizer_preview() ) return; $css = ''; $option = Jetpack_Custom_CSS::is_preview() ? 'safecss_preview' : 'safecss'; if ( 'safecss' == $option ) { if ( Jetpack_Options::get_option_and_ensure_autoload( 'safecss_revision_migrated', '0' ) ) { $safecss_post = Jetpack_Custom_CSS::get_post(); if ( ! empty( $safecss_post['post_content'] ) ) { $css = $safecss_post['post_content']; } } else { $current_revision = Jetpack_Custom_CSS::get_current_revision(); if ( ! empty( $current_revision['post_content'] ) ) { $css = $current_revision['post_content']; } } // Fix for un-migrated Custom CSS if ( empty( $safecss_post ) ) { $_css = Jetpack_Options::get_option_and_ensure_autoload( 'safecss', '' ); if ( !empty( $_css ) ) { $css = $_css; } } } if ( 'safecss_preview' == $option ) { $safecss_post = Jetpack_Custom_CSS::get_current_revision(); if ( !empty( $safecss_post['post_content'] ) ) { $css = $safecss_post['post_content']; } } $css = str_replace( array( '\\\00BB \\\0020', '\0BB \020', '0BB 020' ), '\00BB \0020', $css ); if ( $css == '' ) return; if ( /** * Allow inserting CSS inline instead of through a separate file. * * @module custom-css * * @since 3.4.0 * * @param bool false Should the CSS be added inline instead of through a separate file. Default to false. * @param string $css Custom CSS. */ apply_filters( 'safecss_embed_style', false, $css ) ) { echo "\r\n" . '\r\n"; } else { $href = home_url( '/' ); $href = add_query_arg( 'custom-css', 1, $href ); $href = add_query_arg( 'csblog', $blog_id, $href ); $href = add_query_arg( 'cscache', 6, $href ); $href = add_query_arg( 'csrev', (int) get_option( $option . '_rev' ), $href ); /** * Filter the Custom CSS link enqueued in the head. * * @module custom-css * * @since 1.7.0 * * @param string $href Custom CSS link enqueued in the head. * @param string $blog_id Blog ID. */ $href = apply_filters( 'safecss_href', $href, $blog_id ); if ( Jetpack_Custom_CSS::is_preview() ) $href = add_query_arg( 'csspreview', 'true', $href ); ?> in the element for the custom css stylesheet. * * @module custom-css * * @since 2.2.2 */ do_action( 'safecss_link_tag_post' ); } static function style_filter( $current ) { if ( Jetpack_Custom_CSS::is_freetrial() && ( ! Jetpack_Custom_CSS::is_preview() || ! current_user_can( 'switch_themes' ) ) ) return $current; else if ( Jetpack_Custom_CSS::skip_stylesheet() ) /** * Filter the default blank Custom CSS URL. * * @module custom-css * * @since 2.2.1 * * @param string $url Default blank Custom CSS URL. */ return apply_filters( 'safecss_style_filter_url', plugins_url( 'custom-css/css/blank.css', __FILE__ ) ); return $current; } static function buffer( $html ) { $html = str_replace( '', Jetpack_Custom_CSS::preview_flag(), $html ); return preg_replace_callback( '!href=([\'"])(.*?)\\1!', array( 'Jetpack_Custom_CSS', 'preview_links' ), $html ); } static function preview_links( $matches ) { if ( 0 !== strpos( $matches[2], get_option( 'home' ) ) ) return $matches[0]; $link = wp_specialchars_decode( $matches[2] ); $link = add_query_arg( 'csspreview', 'true', $link ); $link = esc_url( $link ); return "href={$matches[1]}$link{$matches[1]}"; } /** * Places a black bar above every preview page */ static function preview_flag() { if ( is_admin() ) return; $message = esc_html__( 'Preview: changes must be saved or they will be lost', 'jetpack' ); /** * Filter the Preview message displayed on the site when previewing custom CSS, before to save it. * * @module custom-css * * @since 1.7.0 * * @param string $message Custom CSS preview message. */ $message = apply_filters( 'safecss_preview_message', $message ); $preview_flag_js = "var flag = document.createElement('div'); flag.innerHTML = " . json_encode( $message ) . "; flag.style.background = '#FF6600'; flag.style.color = 'white'; flag.style.textAlign = 'center'; flag.style.fontSize = '15px'; flag.style.padding = '2px'; flag.style.fontFamily = 'sans-serif'; document.body.style.paddingTop = '0px'; document.body.insertBefore(flag, document.body.childNodes[0]); "; /** * Filter the Custom CSS preview message JS styling. * * @module custom-css * * @since 1.7.0 * * @param string $preview_flag_js Custom CSS preview message JS styling. */ $preview_flag_js = apply_filters( 'safecss_preview_flag_js', $preview_flag_js ); if ( $preview_flag_js ) { $preview_flag_js = ''; } return $preview_flag_js; } static function menu() { $parent = 'themes.php'; $title = __( 'Edit CSS', 'jetpack' ); $hook = add_theme_page( $title, $title, 'edit_theme_options', 'editcss', array( 'Jetpack_Custom_CSS', 'admin' ) ); add_action( "load-revision.php", array( 'Jetpack_Custom_CSS', 'prettify_post_revisions' ) ); add_action( "load-$hook", array( 'Jetpack_Custom_CSS', 'update_title' ) ); } /** * Adds a menu item in the appearance section for this plugin's administration * page. Also adds hooks to enqueue the CSS and JS for the admin page. */ static function update_title() { global $title; $title = __( 'CSS', 'jetpack' ); } static function prettify_post_revisions() { add_filter( 'the_title', array( 'Jetpack_Custom_CSS', 'post_title' ), 10, 2 ); } static function post_title( $title, $post_id ) { if ( !$post_id = (int) $post_id ) { return $title; } if ( !$post = get_post( $post_id ) ) { return $title; } if ( 'safecss' != $post->post_type ) { return $title; } return __( 'Custom CSS Stylesheet', 'jetpack' ); } static function enqueue_scripts( $hook ) { if ( 'appearance_page_editcss' != $hook ) return; wp_enqueue_script( 'postbox' ); wp_enqueue_script( 'custom-css-editor', Jetpack::get_file_url_for_environment( '_inc/build/custom-css/custom-css/js/css-editor.min.js', 'modules/custom-css/custom-css/js/css-editor.js' ), 'jquery', '20130325', true ); wp_enqueue_style( 'custom-css-editor', plugins_url( 'custom-css/css/css-editor.css', __FILE__ ) ); if ( defined( 'SAFECSS_USE_ACE' ) && SAFECSS_USE_ACE ) { wp_register_style( 'jetpack-css-codemirror', plugins_url( 'custom-css/css/codemirror.css', __FILE__ ), array(), '20120905' ); wp_enqueue_style( 'jetpack-css-use-codemirror', plugins_url( 'custom-css/css/use-codemirror.css', __FILE__ ), array( 'jetpack-css-codemirror' ), '20120905' ); wp_register_script( 'jetpack-css-codemirror', plugins_url( 'custom-css/js/codemirror.min.js', __FILE__ ), array(), '3.16', true ); wp_enqueue_script( 'jetpack-css-use-codemirror', Jetpack::get_file_url_for_environment( '_inc/build/custom-css/custom-css/js/use-codemirror.min.js', 'modules/custom-css/custom-css/js/use-codemirror.js' ), array( 'jquery', 'underscore', 'jetpack-css-codemirror' ), '20131009', true ); } } static function saved_message() { echo '

' . __( 'Stylesheet saved.', 'jetpack' ) . '

'; } static function admin() { add_meta_box( 'submitdiv', __( 'Publish', 'jetpack' ), array( __CLASS__, 'publish_box' ), 'editcss', 'side' ); add_action( 'custom_css_submitbox_misc_actions', array( __CLASS__, 'content_width_settings' ) ); $safecss_post = Jetpack_Custom_CSS::get_post(); if ( ! empty( $safecss_post ) && 0 < $safecss_post['ID'] && wp_get_post_revisions( $safecss_post['ID'], array( 'posts_per_page' => 1 ) ) ) add_meta_box( 'revisionsdiv', __( 'CSS Revisions', 'jetpack' ), array( __CLASS__, 'revisions_meta_box' ), 'editcss', 'side' ); ?>

beginner tutorial. Questions? Ask in the Themes and Templates forum.', 'jetpack' ) ); ?>


More info.)', 'jetpack' ), '', /** * Filter the Custom CSS limited width's support doc URL. * * @module custom-css * * @since 2.2.3 * * @param string $url Custom CSS limited width's support doc URL. */ apply_filters( 'safecss_limit_width_link', 'http://jetpack.com/support/custom-css/#limited-width' ) ); ?>

Name; ?>


$posts_per_page, 'post_type' => 'revision', 'post_status' => 'inherit', 'post_parent' => $safecss_post['ID'], 'orderby' => 'date', 'order' => 'DESC' ) ); if ( $revisions->have_posts() ) { ?> found_posts > 6 && !$show_all_revisions ) { ?>
is_theme_active(); return false; } static function minify( $css, $preprocessor = '' ) { if ( ! $css ) return ''; if ( $preprocessor ) { /** This filter is documented in modules/custom-css/custom-css.php */ $preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() ); if ( isset( $preprocessors[$preprocessor] ) ) { $css = call_user_func( $preprocessors[$preprocessor]['callback'], $css ); } } safecss_class(); $csstidy = new csstidy(); $csstidy->optimise = new safecss( $csstidy ); $csstidy->set_cfg( 'remove_bslash', false ); $csstidy->set_cfg( 'compress_colors', true ); $csstidy->set_cfg( 'compress_font-weight', true ); $csstidy->set_cfg( 'remove_last_;', true ); $csstidy->set_cfg( 'case_properties', true ); $csstidy->set_cfg( 'discard_invalid_properties', true ); $csstidy->set_cfg( 'css_level', 'CSS3.0' ); $csstidy->set_cfg( 'template', 'highest'); $csstidy->parse( $css ); return $csstidy->print->plain(); } /** * When restoring a SafeCSS post revision, also copy over the * content_width and custom_css_add post metadata. */ static function restore_revision( $_post_id, $_revision_id ) { $_post = get_post( $_post_id ); if ( 'safecss' != $_post->post_type ) return; $safecss_revision = Jetpack_Custom_CSS::get_current_revision(); $content_width = get_post_meta( $_revision_id, 'content_width', true ); $custom_css_add = get_post_meta( $_revision_id, 'custom_css_add', true ); $preprocessor = get_post_meta( $_revision_id, 'custom_css_preprocessor', true ); update_metadata( 'post', $safecss_revision['ID'], 'content_width', $content_width ); update_metadata( 'post', $safecss_revision['ID'], 'custom_css_add', $custom_css_add ); update_metadata( 'post', $safecss_revision['ID'], 'custom_css_preprocessor', $preprocessor ); delete_option( 'safecss_add' ); delete_option( 'safecss_content_width' ); update_post_meta( $_post->ID, 'content_width', $content_width ); update_post_meta( $_post->ID, 'custom_css_add', $custom_css_add ); update_post_meta( $_post->ID, 'custom_css_preprocessor', $preprocessor ); delete_option( 'safecss_preview_add' ); } /** * Migration routine for moving safecss from wp_options to wp_posts to support revisions * * @return void */ static function upgrade() { $css = get_option( 'safecss' ); if ( get_option( 'safecss_revision_migrated' ) ) { return false; } // Check if CSS is stored in wp_options if ( $css ) { // Remove the async actions from publish_post remove_action( 'publish_post', 'queue_publish_post' ); $post = array(); $post['post_content'] = $css; $post['post_title'] = 'safecss'; $post['post_status'] = 'publish'; $post['post_type'] = 'safecss'; // Insert the CSS into wp_posts $post_id = wp_insert_post( $post ); // Check for errors if ( !$post_id or is_wp_error( $post_id ) ) die( $post_id->get_error_message() ); // Delete safecss option delete_option( 'safecss' ); } unset( $css ); // Check if we have already done this if ( !get_option( 'safecss_revision_migrated' ) ) { define( 'DOING_MIGRATE', true ); // Get hashes of safecss post and current revision $safecss_post = Jetpack_Custom_CSS::get_post(); if ( empty( $safecss_post ) ) return; $safecss_post_hash = md5( $safecss_post['post_content'] ); $current_revision = Jetpack_Custom_CSS::get_current_revision(); if ( null == $current_revision ) return; $current_revision_hash = md5( $current_revision['post_content'] ); // If hashes are not equal, set safecss post with content from current revision if ( $safecss_post_hash !== $current_revision_hash ) { Jetpack_Custom_CSS::save_revision( $current_revision['post_content'] ); // Reset post_content to display the migrated revsion $safecss_post['post_content'] = $current_revision['post_content']; } // Set option so that we dont keep doing this update_option( 'safecss_revision_migrated', time() ); } $newest_safecss_post = Jetpack_Custom_CSS::get_current_revision(); if ( $newest_safecss_post ) { if ( get_option( 'safecss_content_width' ) ) { // Add the meta to the post and the latest revision. update_post_meta( $newest_safecss_post['ID'], 'content_width', get_option( 'safecss_content_width' ) ); update_metadata( 'post', $newest_safecss_post['ID'], 'content_width', get_option( 'safecss_content_width' ) ); delete_option( 'safecss_content_width' ); } if ( get_option( 'safecss_add' ) ) { update_post_meta( $newest_safecss_post['ID'], 'custom_css_add', get_option( 'safecss_add' ) ); update_metadata( 'post', $newest_safecss_post['ID'], 'custom_css_add', get_option( 'safecss_add' ) ); delete_option( 'safecss_add' ); } } } /** * Adds a filter to the redirect location in `wp-admin/revisions.php`. */ static function add_revision_redirect() { add_filter( 'wp_redirect', array( __CLASS__, 'revision_redirect' ) ); } /** * Filters the redirect location in `wp-admin/revisions.php`. * * @param string $location The path to redirect to. * @return string */ static function revision_redirect( $location ) { $post = get_post(); if ( ! empty( $post->post_type ) && 'safecss' == $post->post_type ) { $location = 'themes.php?page=editcss'; if ( 'edit.php' == $location ) { $location = ''; } } return $location; } static function revision_post_link( $post_link, $post_id, $context ) { if ( !$post_id = (int) $post_id ) { return $post_link; } if ( !$post = get_post( $post_id ) ) { return $post_link; } if ( 'safecss' != $post->post_type ) { return $post_link; } $post_link = admin_url( 'themes.php?page=editcss' ); if ( 'display' == $context ) { return esc_url( $post_link ); } return esc_url_raw( $post_link ); } /** * When on the edit screen, make sure the custom content width * setting is applied to the large image size. */ static function editor_max_image_size( $dims, $size = 'medium', $context = null ) { list( $width, $height ) = $dims; if ( 'large' == $size && 'edit' == $context ) $width = Jetpack::get_content_width(); return array( $width, $height ); } /** * Override the content_width with a custom value if one is set. */ static function jetpack_content_width( $content_width ) { $custom_content_width = 0; if ( Jetpack_Custom_CSS::is_preview() ) { $safecss_post = Jetpack_Custom_CSS::get_current_revision(); $custom_content_width = intval( get_post_meta( $safecss_post['ID'], 'content_width', true ) ); } else if ( ! Jetpack_Custom_CSS::is_freetrial() ) { $custom_css_post_id = Jetpack_Custom_CSS::post_id(); if ( $custom_css_post_id ) $custom_content_width = intval( get_post_meta( $custom_css_post_id, 'content_width', true ) ); } if ( $custom_content_width > 0 ) $content_width = $custom_content_width; return $content_width; } } class Jetpack_Safe_CSS { static function filter_attr( $css, $element = 'div' ) { safecss_class(); $css = $element . ' {' . $css . '}'; $csstidy = new csstidy(); $csstidy->optimise = new safecss( $csstidy ); $csstidy->set_cfg( 'remove_bslash', false ); $csstidy->set_cfg( 'compress_colors', false ); $csstidy->set_cfg( 'compress_font-weight', false ); $csstidy->set_cfg( 'discard_invalid_properties', true ); $csstidy->set_cfg( 'merge_selectors', false ); $csstidy->set_cfg( 'remove_last_;', false ); $csstidy->set_cfg( 'css_level', 'CSS3.0' ); $css = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $css ); $css = wp_kses_split( $css, array(), array() ); $csstidy->parse( $css ); $css = $csstidy->print->plain(); $css = str_replace( array( "\n","\r","\t" ), '', $css ); preg_match( "/^{$element}\s*{(.*)}\s*$/", $css, $matches ); if ( empty( $matches[1] ) ) return ''; return $matches[1]; } } function migrate() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::upgrade()' ); return Jetpack_Custom_CSS::upgrade(); } function safecss_revision_redirect( $redirect ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::revision_redirect()' ); return Jetpack_Custom_CSS::revision_redirect( $redirect ); } function safecss_revision_post_link( $post_link, $post_id, $context ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::revision_post_link()' ); return Jetpack_Custom_CSS::revision_post_link( $post_link, $post_id, $context ); } function get_safecss_post() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::get_post()' ); return Jetpack_Custom_CSS::get_post(); } function custom_css_post_id() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::post_id()' ); return Jetpack_Custom_CSS::post_id(); } function get_current_revision() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::get_current_revision()' ); return Jetpack_Custom_CSS::get_current_revision(); } function save_revision( $css, $is_preview = false, $preprocessor = '' ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::save_revision()' ); return Jetpack_Custom_CSS::save_revision( $css, $is_preview, $preprocessor ); } function safecss_skip_stylesheet() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::skip_stylesheet()' ); return Jetpack_Custom_CSS::skip_stylesheet(); } function safecss_init() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::init()' ); return Jetpack_Custom_CSS::init(); } function safecss_is_preview() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::is_preview()' ); return Jetpack_Custom_CSS::is_preview(); } function safecss_is_freetrial() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::is_freetrial()' ); return Jetpack_Custom_CSS::is_freetrial(); } function safecss( $compressed = false ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::get_css()' ); return Jetpack_Custom_CSS::get_css( $compressed ); } function safecss_print() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::print_css()' ); return Jetpack_Custom_CSS::print_css(); } function safecss_style() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::link_tag()' ); return Jetpack_Custom_CSS::link_tag(); } function safecss_style_filter( $current ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::style_filter()' ); return Jetpack_Custom_CSS::style_filter( $current ); } function safecss_buffer( $html ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::buffer()' ); return Jetpack_Custom_CSS::buffer( $html ); } function safecss_preview_links( $matches ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::preview_links()' ); return Jetpack_Custom_CSS::preview_links( $matches ); } function safecss_preview_flag() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::preview_flag()' ); return Jetpack_Custom_CSS::preview_flag(); } function safecss_menu() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::menu()' ); return Jetpack_Custom_CSS::menu(); } function update_title() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::update_title()' ); return Jetpack_Custom_CSS::update_title(); } function safecss_prettify_post_revisions() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::prettify_post_revisions()' ); return Jetpack_Custom_CSS::prettify_post_revisions(); } function safecss_remove_title_excerpt_from_revisions() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::remove_title_excerpt_from_revisions()' ); return Jetpack_Custom_CSS::remove_title_excerpt_from_revisions(); } function safecss_post_title( $title, $post_id ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::post_title()' ); return Jetpack_Custom_CSS::post_title( $title, $post_id ); } function safe_css_enqueue_scripts() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::enqueue_scripts()' ); return Jetpack_Custom_CSS::enqueue_scripts( null ); } function safecss_admin_head() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::admin_head()' ); return Jetpack_Custom_CSS::admin_head(); } function safecss_saved() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::saved_message()' ); return Jetpack_Custom_CSS::saved_message(); } function safecss_admin() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::admin()' ); return Jetpack_Custom_CSS::admin(); } function custom_css_meta_box() { _deprecated_function( __FUNCTION__, '2.1', 'add_meta_box( $id, $title, $callback, \'editcss\', \'side\' )' ); } function custom_css_post_revisions_meta_box( $safecss_post ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::revisions_meta_box()' ); return Jetpack_Custom_CSS::revisions_meta_box( $safecss_post ); } function disable_safecss_style() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::disable()' ); return Jetpack_Custom_CSS::disable(); } function custom_css_reset() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::reset()' ); return Jetpack_Custom_CSS::reset(); } function custom_css_is_customizer_preview() { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::is_customizer_preview()' ); return Jetpack_Custom_CSS::is_customizer_preview(); } function custom_css_minify( $css, $preprocessor = '' ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::minify()' ); return Jetpack_Custom_CSS::minify( $css, $preprocessor ); } function custom_css_restore_revision( $_post_id, $_revision_id ) { _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::restore_revision()' ); return Jetpack_Custom_CSS::restore_revision( $_post_id, $_revision_id ); } if ( ! function_exists( 'safecss_class' ) ) : function safecss_class() { // Wrapped so we don't need the parent class just to load the plugin if ( class_exists('safecss') ) return; require_once( dirname( __FILE__ ) . '/csstidy/class.csstidy.php' ); class safecss extends csstidy_optimise { function postparse() { /** * Fires after parsing the css. * * @module custom-css * * @since 1.8.0 * * @param obj $this CSSTidy object. */ do_action( 'csstidy_optimize_postparse', $this ); return parent::postparse(); } function subvalue() { /** * Fires before optimizing the Custom CSS subvalue. * * @module custom-css * * @since 1.8.0 * * @param obj $this CSSTidy object. **/ do_action( 'csstidy_optimize_subvalue', $this ); return parent::subvalue(); } } } endif; if ( ! function_exists( 'safecss_filter_attr' ) ) { function safecss_filter_attr( $css, $element = 'div' ) { return Jetpack_Safe_CSS::filter_attr( $css, $element ); } } include_once dirname( __FILE__ ) . '/custom-css/preprocessors.php';