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(); do_action( 'safecss_migrate_post' ); } 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' ) ); } // 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' ); 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 ); 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'] ) { do_action( 'safecss_parse_pre', $csstidy, $css, $args ); $csstidy->parse( $css ); 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; } // Freetrial only. 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() { $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'] = $css; $post['post_title'] = 'safecss'; $post['post_status'] = 'publish'; $post['post_type'] = 'safecss'; $post['post_content_filtered'] = $compressed_css; // Set excerpt to current theme, for display in revisions list if ( function_exists( 'wp_get_theme' ) ) { $current_theme = wp_get_theme(); $post['post_excerpt'] = $current_theme->Name; } else { $post['post_excerpt'] = get_current_theme(); } // 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 if ( function_exists( 'wp_get_theme' ) ) { $current_theme = wp_get_theme(); $safecss_post['post_excerpt'] = $current_theme->Name; } else { $safecss_post['post_excerpt'] = get_current_theme(); } // 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 ) { $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() { $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) ( get_option( 'safecss_add' ) == 'no' ); } } } static function is_preview() { return isset( $_GET['csspreview'] ) && $_GET['csspreview'] === 'true'; } /* * False when the site has the Custom Design upgrade. * Used only on WordPress.com. */ static function is_freetrial() { return apply_filters( 'safecss_is_freetrial', false ); } static function get_css( $compressed = 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'; if ( 'safecss' == $option ) { if ( get_option( 'safecss_revision_migrated' ) ) { $safecss_post = Jetpack_Custom_CSS::get_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 = stripslashes( $css ); $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( apply_filters( 'safecss_default_css', __( "Welcome to Custom CSS!\n\nCSS (Cascading Style Sheets) is a kind of code that tells the browser how to render a web page. You may delete these comments and get started with your customizations.\n\nBy default, your stylesheet will be loaded after the theme stylesheets, which means that your rules can take precedence and override the theme CSS rules. Just write here what you want to change, you don't need to copy all your theme's stylesheet content.", 'jetpack' ) ) ) . "\n*/"; } $css = apply_filters( 'safecss_css', $css ); return $css; } static function print_css() { do_action( 'safecss_print_pre' ); echo Jetpack_Custom_CSS::get_css( true ); } static function link_tag() { global $blog_id, $current_blog; if ( 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 ( get_option( 'safecss_revision_migrated' ) ) { $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 = get_option( '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; $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 ); $href = apply_filters( 'safecss_href', $href, $blog_id ); if ( Jetpack_Custom_CSS::is_preview() ) $href = add_query_arg( 'csspreview', 'true', $href ); ?> ', 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' ); $message = apply_filters( 'safecss_preview_message', $message ); $preview_flag_js = "var flag = document.createElement('div'); flag.innerHTML = " . json_encode( $message ) . "; flag.style.background = 'black'; flag.style.color = 'white'; flag.style.textAlign = 'center'; flag.style.fontSize = '15px'; flag.style.padding = '1px'; document.body.style.paddingTop = '32px'; document.body.insertBefore(flag, document.body.childNodes[0]); "; $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', plugins_url( 'custom-css/js/css-editor.js', __FILE__ ), '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', plugins_url( 'custom-css/js/use-codemirror.js', __FILE__ ), array( 'jquery', 'underscore', 'jetpack-css-codemirror' ), '20131009', true ); } } static function saved_message() { echo '
' . __( 'Stylesheet saved.', 'jetpack' ) . '