PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.5.2
SiteOrigin CSS v1.5.2
1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.10 1.5.11 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0
so-css / so-css.php
so-css Last commit date
css 4 years ago inc 4 years ago js 4 years ago lang 4 years ago lib 4 years ago tpl 4 years ago LICENSE 11 years ago readme.txt 4 years ago so-css.php 4 years ago
so-css.php
844 lines
1 <?php
2 /*
3 Plugin Name: SiteOrigin CSS
4 Description: An advanced CSS editor from SiteOrigin.
5 Version: 1.5.2
6 Author: SiteOrigin
7 Author URI: https://siteorigin.com
8 Plugin URI: https://siteorigin.com/css/
9 License: GPL3
10 License URI: https://www.gnu.org/licenses/gpl-3.0.txt
11 Text Domain: so-css
12 */
13
14 // Handle the legacy CSS editor that came with SiteOrigin themes
15 include plugin_dir_path( __FILE__ ) . 'inc/legacy.php';
16
17 define( 'SOCSS_VERSION', '1.5.2' );
18 define( 'SOCSS_JS_SUFFIX', '.min' );
19
20 /**
21 * Class SiteOrigin_CSS The main class for the SiteOrigin CSS Editor
22 */
23 class SiteOrigin_CSS {
24 private $theme;
25 private $snippet_paths;
26
27 function __construct() {
28 $this->theme = basename( get_template_directory() );
29 $this->snippet_paths = array();
30
31 // Main header actions
32 add_action( 'plugins_loaded', array( $this, 'set_plugin_textdomain' ) );
33
34 // Priority 20 is necessary to ensure our CSS takes precedence.
35 add_action( 'wp_head', array( $this, 'enqueue_css' ), 20 );
36
37 // All the admin actions
38 add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
39 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ), 20 );
40 add_action( 'admin_enqueue_scripts', array( $this, 'dequeue_admin_scripts' ), 19 );
41 add_action( 'load-appearance_page_so_custom_css', array( $this, 'add_help_tab' ) );
42
43 // Add the action links.
44 add_action( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
45
46 // The request to hide the getting started video
47 add_action( 'wp_ajax_socss_hide_getting_started', array( $this, 'admin_action_hide_getting_started' ) );
48
49 add_action( 'wp_ajax_socss_get_post_css', array( $this, 'admin_action_get_post_css' ) );
50 add_action( 'wp_ajax_socss_get_revisions_list', array( $this, 'admin_action_get_revisions_list' ) );
51 add_action( 'wp_ajax_socss_save_css', array( $this, 'admin_action_save_css' ) );
52
53 if ( ! is_admin() ) {
54 if( isset( $_GET['so_css_preview'] ) ) {
55
56 add_action( 'plugins_loaded', array($this, 'disable_ngg_resource_manager') );
57 add_filter( 'show_admin_bar', '__return_false' );
58 add_filter( 'wp_enqueue_scripts', array($this, 'enqueue_inspector_scripts') );
59 add_filter( 'wp_footer', array($this, 'inspector_templates') );
60
61 // We'll be grabbing all the enqueued scripts and outputting them
62 add_action( 'wp_enqueue_scripts', array($this, 'inline_inspector_scripts'), 100 );
63 }
64 }
65 }
66
67 /**
68 * Get a singleton of the SiteOrigin CSS.
69 *
70 * @return SiteOrigin_CSS
71 */
72 static function single() {
73 static $single;
74
75 if ( empty( $single ) ) {
76 $single = new SiteOrigin_CSS();
77 }
78
79 return $single;
80 }
81
82 /**
83 * Retrieve the current custom CSS for a given theme and post id combination.
84 *
85 * @param $theme string The name of the theme for which to retrieve custom CSS.
86 * @param $post_id int The ID of the specific post for which to retrieve custom CSS.
87 *
88 * @return string The custom CSS for the theme and post ID combination.
89 */
90 function get_custom_css( $theme, $post_id = null ) {
91 $css_key = 'siteorigin_custom_css[' . $theme . ']';
92 if ( empty( $post_id ) ) {
93 return get_option( $css_key, '' );
94 }
95
96 return get_post_meta( $post_id, $css_key, true );
97 }
98
99 /**
100 * Save custom CSS for a given theme and post id combination.
101 *
102 * @param $custom_css string The custom CSS to save.
103 * @param $theme string The name of the theme for which to save custom CSS.
104 * @param $post_id int The ID of the specific post for which to save custom CSS.
105 *
106 * @return bool Whether or not saving the custom CSS was successful.
107 */
108 function save_custom_css( $custom_css, $theme, $post_id = null ) {
109 $css_key = 'siteorigin_custom_css[' . $theme . ']';
110 if ( empty( $post_id ) ) {
111 $current = get_option( $css_key );
112 if ( $current === false ) {
113 return add_option( $css_key, $custom_css, '', 'no' );
114 } else {
115 return update_option( $css_key, $custom_css );
116 }
117 }
118
119 if ( metadata_exists( 'post', $post_id, $css_key ) ) {
120 return update_post_meta( $post_id, $css_key, $custom_css );
121 }
122
123 return add_post_meta( $post_id, $css_key, $custom_css );
124 }
125
126 /**
127 * Save custom CSS for a given theme and post id combination to a file in the uploads directory to allow for caching.
128 *
129 * @param $custom_css
130 * @param $theme
131 * @param null $post_id
132 */
133 function save_custom_css_file( $custom_css, $theme, $post_id = null ) {
134 if ( WP_Filesystem() ) {
135 global $wp_filesystem;
136 $upload_dir = wp_upload_dir();
137 $upload_dir_path = $upload_dir['basedir'] . '/so-css/';
138
139 if ( ! $wp_filesystem->is_dir( $upload_dir_path ) ) {
140 $wp_filesystem->mkdir( $upload_dir_path );
141 }
142
143 $css_file_name = 'so-css-' . $theme . ( ! empty( $post_id ) ? '_' . $post_id : '' );
144 $css_file_path = $upload_dir_path . $css_file_name . '.css';
145
146 if ( file_exists( $css_file_path ) ) {
147 $wp_filesystem->delete( $css_file_path );
148 }
149
150 $wp_filesystem->put_contents(
151 $css_file_path,
152 $custom_css
153 );
154 }
155 }
156
157 /**
158 * Retrieve the previous revisions of custom CSS for a given theme and post id combination.
159 *
160 * @param $theme string The name of the theme for which to retrieve custom CSS revisions.
161 * @param $post_id int The ID of the specific post for which to retrieve custom CSS revisions.
162 *
163 * @return array The custom CSS revisions for the theme and post ID combination.
164 */
165 function get_custom_css_revisions( $theme, $post_id = null ) {
166 $css_key = 'siteorigin_custom_css_revisions[' . $theme . ']';
167 if ( empty( $post_id ) ) {
168 return get_option( $css_key, '' );
169 }
170
171 return get_post_meta( $post_id, $css_key, true );
172 }
173
174 /**
175 * Adds a custom CSS revision for a given theme and post id combination.
176 *
177 * @param $custom_css string The custom CSS to add as a revision.
178 * @param $theme string The name of the theme for which to save custom CSS.
179 * @param $post_id int The ID of the specific post for which to save custom CSS.
180 *
181 * @return bool Whether or not adding the custom CSS revision was successful.
182 */
183 function add_custom_css_revision( $custom_css, $theme, $post_id = null ) {
184 $revisions = $this->get_custom_css_revisions( $this->theme, $post_id );
185
186 $css_key = 'siteorigin_custom_css_revisions[' . $theme . ']';
187
188 if ( empty( $revisions ) ) {
189 $revisions = array();
190 if ( empty( $post_id ) ) {
191 add_option( $css_key, $revisions, '', 'no' );
192 } else {
193 add_post_meta( $post_id, $css_key, $revisions );
194 }
195 }
196 $revisions[ time() ] = $custom_css;
197
198 // Sort the revisions and cut off any old ones.
199 krsort( $revisions );
200 $revisions = array_slice( $revisions, 0, 15, true );
201
202 if ( empty( $post_id ) ) {
203 return update_option( $css_key, $revisions );
204 }
205
206 return update_post_meta( $post_id, $css_key, $revisions );
207 }
208
209 /**
210 * Enqueue or print inline CSS.
211 */
212 function enqueue_css() {
213
214 $this->enqueue_custom_css( $this->theme );
215
216 if ( is_singular() ) {
217 $this->enqueue_custom_css( $this->theme, get_the_ID() );
218 }
219
220 }
221
222 /**
223 * Enqueue the custom CSS for the given theme and post id combination.
224 *
225 * @param $theme string The name of the theme for which to enqueue custom CSS.
226 * @param $post_id int The ID of the specific post for which to enqueue custom CSS.
227 *
228 */
229 function enqueue_custom_css( $theme, $post_id = null ) {
230
231 $upload_dir = wp_upload_dir();
232 $upload_dir_path = $upload_dir['basedir'] . '/so-css/';
233
234 $css_id = $theme . ( ! empty( $post_id ) ? '_' . $post_id : '' );
235 $css_file_name = 'so-css-' . $css_id;
236 $css_file_path = $upload_dir_path . $css_file_name . '.css';
237
238 if ( empty( $_GET['so_css_preview'] ) && ! is_admin() && file_exists( $css_file_path ) ) {
239 wp_enqueue_style(
240 'so-css-' . $css_id,
241 set_url_scheme( $upload_dir['baseurl'] . '/so-css/' . $css_file_name . '.css' ),
242 array(),
243 $this->get_latest_revision_timestamp()
244 );
245 } else {
246 $custom_css = $this->get_custom_css( $theme, $post_id );
247 // We just need to enqueue a dummy style
248 if ( ! empty( $custom_css ) ) {
249 echo "<style id='" . sanitize_html_class( $css_id ) . "-custom-css' class='siteorigin-custom-css' type='text/css'>\n";
250 echo self::sanitize_css( $custom_css ) . "\n";
251 echo "</style>\n";
252 }
253 }
254 }
255
256 function set_plugin_textdomain() {
257 load_plugin_textdomain( 'so-css', false, plugin_dir_path( __FILE__ ) . 'lang/' );
258 }
259
260 /**
261 * Action to run on the admin action.
262 */
263 function action_admin_menu() {
264 add_theme_page( esc_html__( 'Custom CSS', 'so-css' ), esc_html__( 'Custom CSS', 'so-css' ), 'edit_theme_options', 'so_custom_css', array(
265 $this,
266 'display_admin_page'
267 ) );
268
269 if ( current_user_can( 'edit_theme_options' ) && isset( $_POST['siteorigin_custom_css'] ) ) {
270 check_admin_referer( 'custom_css', '_sononce' );
271
272 // Sanitize CSS input. Should keep most tags, apart from script and style tags.
273 $custom_css = self::sanitize_css( filter_input( INPUT_POST, 'siteorigin_custom_css' ) );
274 $socss_post_id = filter_input( INPUT_GET, 'socss_post_id', FILTER_VALIDATE_INT );
275
276 $current = $this->get_custom_css( $this->theme, $socss_post_id );
277 $this->save_custom_css( $custom_css, $this->theme, $socss_post_id );
278
279 // If this has changed, then add a revision.
280 if ( $current != $custom_css ) {
281 $this->add_custom_css_revision( $custom_css, $this->theme, $socss_post_id );
282
283 $this->save_custom_css_file( $custom_css, $this->theme, $socss_post_id );
284 }
285
286 // Update Editor Theme.
287 if (
288 $_POST['so_css_editor_theme'] == 'neat' ||
289 $_POST['so_css_editor_theme'] == 'ambiance'
290 ) {
291 update_option( 'so_css_editor_theme', $_POST['so_css_editor_theme'] );
292 }
293 }
294 }
295
296 /**
297 * Display the help tab
298 */
299 function add_help_tab() {
300 $screen = get_current_screen();
301 $screen->add_help_tab( array(
302 'id' => 'custom-css',
303 'title' => esc_html__( 'Custom CSS', 'so-css' ),
304 'content' => '<p>'
305 . sprintf( esc_html__( "SiteOrigin CSS adds any custom CSS you enter here into your site's header. ", 'so-css' ) )
306 . esc_html__( "These changes will persist across updates so it's best to make all your changes here. ", 'so-css' )
307 . '</p>'
308 ) );
309 }
310
311 function enqueue_admin_scripts( $page ) {
312 if ( $page != 'appearance_page_so_custom_css' ) {
313 return;
314 }
315
316 // Core WordPress stuff that we use
317 wp_enqueue_media();
318
319 global $wp_version;
320 if ( version_compare( $wp_version, '4.9', '>=' ) && wp_get_current_user()->syntax_highlighting ) {
321 wp_enqueue_code_editor( array(
322 'type' => 'css',
323 'codemirror' => array(
324 'lint' => true,
325 ),
326 )
327 );
328 } else {
329 $this->enqueue_fallback_codemirror();
330 }
331 wp_enqueue_style( 'socss-codemirror-theme-neat', plugin_dir_url( __FILE__ ) . 'lib/codemirror/theme/neat.css', array(), SOCSS_VERSION );
332 wp_enqueue_style( 'socss-codemirror-theme-ambiance', plugin_dir_url( __FILE__ ) . 'lib/codemirror/theme/ambiance.css', array(), SOCSS_VERSION );
333
334 // Enqueue the scripts for theme CSS processing
335 wp_enqueue_script( 'siteorigin-css-parser-lib', plugin_dir_url( __FILE__ ) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
336
337 // There are conflicts between CSS linting and the built in WordPress color picker, so use something else
338 wp_enqueue_style( 'siteorigin-custom-css-minicolors', plugin_dir_url( __FILE__ ) . 'lib/minicolors/jquery.minicolors.css', array(), '2.1.7' );
339 wp_enqueue_script( 'siteorigin-custom-css-minicolors', plugin_dir_url( __FILE__ ) . 'lib/minicolors/jquery.minicolors' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), '2.1.7' );
340
341 // URI parsing for preview navigation
342 wp_enqueue_script( 'siteorigin-uri', plugin_dir_url( __FILE__ ) . 'js/URI' . SOCSS_JS_SUFFIX . '.js', array(), SOCSS_VERSION, true );
343
344 // All the custom SiteOrigin CSS stuff
345 wp_enqueue_script( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'js/editor' . SOCSS_JS_SUFFIX . '.js', array( 'jquery', 'underscore', 'backbone' ), SOCSS_VERSION, true );
346 wp_enqueue_style( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'css/admin.css', array( ), SOCSS_VERSION );
347
348
349 // Pretty confusing, but it seems we should be using `home_url` and NOT `site_url`
350 // as described here => https://wordpress.stackexchange.com/a/50605
351 $init_url = home_url();
352
353 if ( ! empty( $socss_post_id ) && is_int( $socss_post_id ) ) {
354 $init_url = set_url_scheme( get_permalink( $socss_post_id ) );
355 }
356
357 $open_visual_editor = ! empty( $_REQUEST['open_visual_editor'] );
358
359 $home_url = add_query_arg( 'so_css_preview', '1', $init_url );
360
361 wp_localize_script( 'siteorigin-custom-css', 'socssOptions', array(
362 'homeURL' => $home_url,
363 'postCssUrlRoot' => wp_nonce_url( admin_url('admin-ajax.php?action=socss_get_post_css'), 'get_post_css' ),
364 'getRevisionsListAjaxUrl' => wp_nonce_url( admin_url('admin-ajax.php?action=socss_get_revisions_list'), 'get_revisions_list' ),
365 'ajaxurl' => wp_nonce_url( admin_url( 'admin-ajax.php' ), 'so-css-ajax' ),
366 'openVisualEditor' => $open_visual_editor,
367
368 'propertyControllers' => apply_filters( 'siteorigin_css_property_controllers', $this->get_property_controllers() ),
369
370 'loc' => array(
371 'unchanged' => esc_html__( 'Unchanged', 'so-css' ),
372 'select' => esc_html__( 'Select', 'so-css' ),
373 'select_image' => esc_html__( 'Select Image', 'so-css' ),
374 'leave' => esc_html__( 'Are you sure you want to leave without saving?', 'so-css' ),
375 )
376 ) );
377
378 // This is for the templates required by the CSS editor. Ideally this would be out in the footer, but we need
379 // it earlier for dependent scripts.
380 include plugin_dir_path( __FILE__ ) . 'tpl/js-templates.php';
381 }
382
383 // Handles loading the fallback version of CodeMirror.
384 function enqueue_fallback_codemirror() {
385 // Enqueue the codemirror scripts. Call Underscore and Backbone dependencies so they're enqueued first to prevent conflicts.
386 wp_enqueue_script( 'socss-codemirror', plugin_dir_url( __FILE__ ) . 'lib/codemirror/lib/codemirror' . SOCSS_JS_SUFFIX . '.js', array( 'underscore', 'backbone' ), '5.2.0' );
387 wp_enqueue_script( 'socss-codemirror-mode-css', plugin_dir_url( __FILE__ ) . 'lib/codemirror/mode/css/css' . SOCSS_JS_SUFFIX . '.js', array(), '5.2.0' );
388
389 // Add in all the linting libs
390 wp_enqueue_script( 'socss-codemirror-lint', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/lint/lint' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.2.0' );
391 wp_enqueue_script( 'socss-codemirror-lint-css', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/lint/css-lint' . SOCSS_JS_SUFFIX . '.js', array(
392 'socss-codemirror',
393 'socss-codemirror-lint-css-lib'
394 ), '5.2.0' );
395 wp_enqueue_script( 'socss-codemirror-lint-css-lib', plugin_dir_url( __FILE__ ) . 'js/csslint' . SOCSS_JS_SUFFIX . '.js', array(), '0.10.0' );
396
397 // The CodeMirror autocomplete library
398 wp_enqueue_script( 'socss-codemirror-show-hint', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/hint/show-hint' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.2.0' );
399
400 // CodeMirror search and dialog addons
401 wp_enqueue_script( 'socss-codemirror-dialog', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/dialog/dialog' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.2.0' );
402
403 wp_enqueue_script( 'socss-codemirror-search', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/search' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.37.0' );
404 wp_enqueue_script( 'socss-codemirror-search-searchcursor', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/searchcursor' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
405 wp_enqueue_script( 'socss-codemirror-search-match-cursor', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/match-highlighter' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
406 wp_enqueue_script( 'socss-codemirror-search-matchesonscrollbar', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/matchesonscrollbar' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
407 wp_enqueue_script( 'socss-codemirror-scroll-annotatescrollbar', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/scroll/annotatescrollbar' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search', 'socss-codemirror-search-matchesonscrollbar' ), '5.37.0' );
408 wp_enqueue_script( 'socss-codemirror-jump-to-line', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/jump-to-line' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
409
410 // All the CodeMirror styles
411 wp_enqueue_style( 'socss-codemirror', plugin_dir_url( __FILE__ ) . 'lib/codemirror/lib/codemirror.css', array(), '5.2.0' );
412 wp_enqueue_style( 'socss-codemirror-lint-css', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/lint/lint.css', array(), '5.2.0' );
413 wp_enqueue_style( 'socss-codemirror-show-hint', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/hint/show-hint.css', array(), '5.2.0' );
414 wp_enqueue_style( 'socss-codemirror-dialog', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/dialog/dialog.css', '5.2.0' );
415 wp_enqueue_style( 'socss-codemirror-search-matchesonscrollbar', plugin_dir_url(__FILE__ ) . 'lib/codemirror/addon/search/matchesonscrollbar.css', array(), '5.37.0' );
416 }
417
418 /**
419 * @param $page
420 */
421 function dequeue_admin_scripts( $page ) {
422 if ( $page != 'appearance_page_so_custom_css' ) {
423 return;
424 }
425
426 // Dequeue the core WordPress color picker on the custom CSS page.
427 // This script causes conflicts and other plugins seem to be enqueueing it on the SO CSS admin page.
428 wp_dequeue_script( 'wp-color-picker' );
429 wp_dequeue_style( 'wp-color-picker' );
430 }
431
432 /**
433 * Get all the available property controllers
434 */
435 function get_property_controllers() {
436 return include plugin_dir_path( __FILE__ ) . 'inc/controller-config.php';
437 }
438
439 function plugin_action_links( $links ) {
440 if ( isset( $links['edit'] ) ) {
441 unset( $links['edit'] );
442 }
443 $links['css_editor'] = '<a href="' . admin_url( 'themes.php?page=so_custom_css' ) . '">' . esc_html__( 'CSS Editor', 'so-css' ) . '</a>';
444 $links['support'] = '<a href="https://siteorigin.com/thread/" target="_blank">' . esc_html__( 'Support', 'so-css' ) . '</a>';
445 if ( apply_filters( 'siteorigin_premium_upgrade_teaser', true ) && ! defined( 'SITEORIGIN_PREMIUM_VERSION' ) ) {
446 $links['addons'] = '<a href="https://siteorigin.com/downloads/premium/?featured_addon=plugin/web-font-selector" style="color: #3db634" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Addons', 'so-css' ) . '</a>';
447 }
448 return $links;
449 }
450
451 function display_admin_page() {
452
453 $socss_post_id = filter_input( INPUT_GET, 'socss_post_id', FILTER_VALIDATE_INT );
454 $theme = filter_input( INPUT_GET, 'theme' );
455 $time = filter_input( INPUT_GET, 'time', FILTER_VALIDATE_INT );
456
457 $page_title = esc_html__( 'SiteOrigin CSS', 'so-css' );
458 $theme_obj = wp_get_theme();
459 $theme_name = $theme_obj->get( 'Name' );
460 $editor_description = sprintf( esc_html__( 'Changes apply to %s and its child themes', 'so-css' ), $theme_name );
461 $save_button_label = esc_html__( 'Save CSS', 'so-css' );
462 $form_save_url = admin_url( 'themes.php?page=so_custom_css' );
463
464 if ( ! empty( $socss_post_id ) ) {
465 $selected_post = get_post( $socss_post_id );
466
467 $page_title = sprintf(
468 esc_html__( 'Editing CSS for: %s', 'so-css' ),
469 $selected_post->post_title
470 );
471
472 $editor_description = sprintf(
473 esc_html__( 'Changes apply to the %s %s when the current theme is %s or its child themes', 'so-css' ),
474 $selected_post->post_type,
475 $selected_post->post_title,
476 $theme_name
477 );
478 $post_type_obj = get_post_type_object( $selected_post->post_type );
479 $post_type_labels = $post_type_obj->labels;
480 $save_button_label = sprintf( esc_html__( 'Save %s CSS', 'so-css' ), $post_type_labels->singular_name );
481 $form_save_url = add_query_arg( 'socss_post_id', urlencode( $socss_post_id ), $form_save_url );
482 }
483 $custom_css = $this->get_custom_css( $this->theme, $socss_post_id );
484 $custom_css_revisions = $this->get_custom_css_revisions( $this->theme, $socss_post_id );
485 $current_revision = 0;
486
487 if ( ! empty( $theme ) && $theme == $this->theme && ! empty( $time ) && ! empty( $custom_css_revisions[ $time ] ) ) {
488 $current_revision = $time;
489 $custom_css = $custom_css_revisions[ $time ];
490 }
491
492 if ( ! empty ( $current_revision ) ) {
493 $save_button_label = esc_html__( 'Revert to this revision', 'so-css' );
494 }
495
496 if ( ! empty( $custom_css_revisions ) ) {
497 krsort( $custom_css_revisions );
498 }
499
500 $theme = basename( get_template_directory() );
501
502 $editor_theme = get_option( 'so_css_editor_theme', 'neat' );
503
504 include plugin_dir_path( __FILE__ ) . 'tpl/page.php';
505 }
506
507
508 function display_teaser() {
509 return apply_filters( 'siteorigin_premium_upgrade_teaser', true ) &&
510 ! defined( 'SITEORIGIN_PREMIUM_VERSION' );
511 }
512
513 /**
514 * Generates the url to edit the custom CSS for a post.
515 */
516 function get_edit_css_link( $post ) {
517 $url = admin_url( 'themes.php?page=so_custom_css' );
518 if ( ! is_int( $post ) ) {
519 $post = get_post( $post );
520 $post = $post->ID;
521 }
522
523 return empty( $post ) ? $url : add_query_arg( array(
524 'socss_post_id' => urlencode( $post ),
525 'open_visual_editor' => 1,
526 ), $url );
527 }
528 /**
529 *
530 */
531 function admin_action_hide_getting_started() {
532 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'hide' ) ) {
533 return;
534 }
535
536 $user = wp_get_current_user();
537 if ( ! empty( $user ) ) {
538 update_user_meta( $user->ID, 'socss_hide_gs', true );
539 }
540 }
541
542 /**
543 * Retrieves the post specific CSS for the supplied postId.
544 */
545 function admin_action_get_post_css() {
546 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'get_post_css' ) ) {
547 wp_die(
548 esc_html__( 'The supplied nonce is invalid.', 'so-css' ),
549 esc_html__( 'Invalid nonce.', 'so-css' ),
550 403
551 );
552 }
553
554 $post_id = filter_input( INPUT_GET, 'postId', FILTER_VALIDATE_INT );
555
556 $current = $this->get_custom_css( $this->theme, $post_id );
557
558 $url = empty( $post_id ) ? home_url() : set_url_scheme( get_permalink( $post_id ) );
559
560 wp_send_json( array( 'css' => empty( $current ) ? '' : $current, 'postUrl' => $url ) );
561 }
562
563 /**
564 * Retrieves the past revisions of post specific CSS for the supplied postId.
565 */
566 function admin_action_get_revisions_list() {
567 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'get_revisions_list' ) ) {
568 wp_die(
569 esc_html__( 'The supplied nonce is invalid.', 'so-css' ),
570 esc_html__( 'Invalid nonce.', 'so-css' ),
571 403
572 );
573 }
574
575 $post_id = filter_input( INPUT_GET, 'postId', FILTER_VALIDATE_INT );
576
577 $this->custom_css_revisions_list( $this->theme, $post_id );
578
579 wp_die();
580 }
581
582 /**
583 * Retrieves the past revisions of post specific CSS for the supplied postId.
584 */
585 function admin_action_save_css() {
586 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'so-css-ajax' ) ) {
587 wp_die(
588 esc_html__( 'The supplied nonce is invalid.', 'so-css' ),
589 esc_html__( 'Invalid nonce.', 'so-css' ),
590 403
591 );
592 }
593
594 if ( current_user_can( 'edit_theme_options' ) && isset( $_POST['css'] ) ) {
595 // Sanitize CSS input. Should keep most tags, apart from script and style tags.
596 $custom_css = self::sanitize_css( stripslashes( $_POST['css'] ) );
597
598 $current = $this->get_custom_css( $this->theme );
599 $this->save_custom_css( $custom_css, $this->theme );
600
601 // If this has changed, then add a revision.
602 if ( $current != $custom_css ) {
603 $this->add_custom_css_revision( $custom_css, $this->theme );
604 $this->save_custom_css_file( $custom_css, $this->theme );
605
606 // Output the full revisions list.
607 $this->custom_css_revisions_list( $this->theme );
608 }
609 }
610 wp_die();
611 }
612
613 function custom_css_revisions_list( $theme, $post_id = null, $current_revision = null ) {
614
615 $revisions = $this->get_custom_css_revisions( $theme, $post_id );
616
617 if ( is_array( $revisions ) && ! empty( $revisions ) ) {
618 $i = 0;
619 foreach ( $revisions as $time => $css ) {
620 $is_current = ( empty( $current_revision ) && $i == 0 ) || ( ! empty( $current_revision ) && $time == $current_revision );
621 $query_args = array( 'theme' => $theme, 'time' => $time, 'open_visual_editor' => false );
622 if ( ! empty( $post_id ) ) {
623 $query_args['socss_post_id'] = $post_id;
624 }
625 ?>
626 <li>
627 <?php if ( ! $is_current ) : ?>
628 <a href="<?php echo esc_url( add_query_arg( $query_args, admin_url( 'themes.php?page=so_custom_css' ) ) ) ?>"
629 class="load-css-revision">
630 <?php endif; ?>
631 <?php echo date('j F Y @ H:i:s', $time + get_option('gmt_offset') * 60 * 60) ?>
632 <?php if ( ! $is_current ) : ?>
633 </a>
634 <?php endif; ?>
635 (<?php printf( esc_html__( '%d chars', 'so-css' ), strlen( $css ) ) ?>)<?php if ( $i == 0 ) : ?> (<?php esc_html_e( 'Latest', 'so-css' ); ?>)<?php endif; ?>
636 </li>
637 <?php
638 $i++;
639 }
640 } else {
641 printf( '<em>%s</em>', esc_html__( 'No revisions yet.', 'so-css' ) );
642 }
643 }
644
645 /**
646 * Add one or more paths to the registered snippet paths
647 *
648 * @param string|array $path
649 */
650 function register_snippet_path( $path ) {
651 $this->snippet_paths = array_merge( $this->snippet_paths, (array) $path );
652 }
653
654 /**
655 * Get all the available snippets
656 *
657 * @return array|bool
658 */
659 function get_snippets() {
660 // Get the snippet paths
661 $snippet_paths = apply_filters( 'siteorigin_css_snippet_paths', $this->snippet_paths );
662 if ( empty( $snippet_paths ) ) {
663 return array();
664 }
665
666 static $snippets = array();
667 if ( ! empty( $snippets ) ) {
668 return $snippets;
669 }
670
671 if ( ! WP_Filesystem() ) {
672 return false;
673 }
674 global $wp_filesystem;
675 foreach ( $snippet_paths as $path ) {
676 foreach ( glob( $path . '/*.css' ) as $css_file ) {
677 $data = get_file_data( $css_file, array(
678 'Name' => 'Name',
679 'Description' => 'Description',
680 ) );
681
682 // Get the CSS and strip out the first header
683 $css = $wp_filesystem->get_contents( $css_file );
684 $css = preg_replace( '!/\*.*?\*/!s', '', $css, 1 );
685
686 $snippets[] = wp_parse_args( $data, array(
687 'css' => str_replace( "\t", ' ', trim( $css ) ),
688 ) );
689 }
690 }
691
692 usort( $snippets, array( $this, 'sort_snippet_callback' ) );
693
694 return $snippets;
695 }
696
697 /**
698 * Sort snippets by name.
699 *
700 * @param $a
701 * @param $b
702 *
703 * @return int
704 */
705 static function sort_snippet_callback( $a, $b ) {
706 return $a['Name'] > $b['Name'] ? 1 : - 1;
707 }
708
709 /**
710 * A very simple CSS sanitization function.
711 *
712 * @param $css
713 *
714 * @return string
715 */
716 static function sanitize_css( $css ) {
717 return trim( strip_tags( $css ) );
718 }
719
720 /**
721 * Get all the available theme CSS
722 */
723 function get_theme_css() {
724 $css = '';
725 if ( file_exists( get_template_directory() . '/style.css' ) ) {
726 $css .= file_get_contents( get_template_directory() . '/style.css' );
727 }
728
729 if ( is_child_theme() ) {
730 $css .= file_get_contents( get_stylesheet_directory() . '/style.css' );
731 }
732
733 // Remove all CSS comments
734 $regex = array(
735 "`^([\t\s]+)`ism" => '',
736 "`^\/\*(.+?)\*\/`ism" => "",
737 "`(\A|[\n;]+)/\*[^*]*\*+(?:[^/*][^*]*\*+)*/`" => "$1",
738 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n"
739 );
740 $css = preg_replace( array_keys( $regex ), $regex, $css );
741 $css = preg_replace( '/\s+/', ' ', $css );
742
743 return $css;
744 }
745
746 function enqueue_inspector_scripts() {
747 if ( ! current_user_can( 'edit_theme_options' ) ) {
748 return;
749 }
750
751 wp_enqueue_style( 'dashicons' );
752
753 wp_enqueue_script( 'siteorigin-css-parser-lib', plugin_dir_url( __FILE__ ) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
754
755 wp_enqueue_script( 'siteorigin-css-sizes', plugin_dir_url( __FILE__ ) . 'js/jquery.sizes' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), '0.33' );
756 wp_enqueue_script( 'siteorigin-css-specificity', plugin_dir_url( __FILE__ ) . 'js/specificity' . SOCSS_JS_SUFFIX . '.js', array() );
757 wp_enqueue_script( 'siteorigin-css-inspector', plugin_dir_url( __FILE__ ) . 'js/inspector' . SOCSS_JS_SUFFIX . '.js', array(
758 'jquery',
759 'underscore',
760 'backbone'
761 ), SOCSS_VERSION, true );
762 wp_enqueue_style( 'siteorigin-css-inspector', plugin_dir_url( __FILE__ ) . 'css/inspector.css', array(), SOCSS_VERSION );
763
764 wp_localize_script( 'siteorigin-css-inspector', 'socssOptions', array() );
765 }
766
767 function inspector_templates() {
768 if ( ! current_user_can( 'edit_theme_options' ) ) {
769 return;
770 }
771
772 include plugin_dir_path( __FILE__ ) . 'tpl/inspector-templates.php';
773 }
774
775 /**
776 * Change the stylesheets to all be inline
777 */
778 function inline_inspector_scripts() {
779 if ( ! current_user_can( 'edit_theme_options' ) ) {
780 return;
781 }
782
783 $regex = array(
784 "`^([\t\s]+)`ism" => '',
785 "`^\/\*(.+?)\*\/`ism" => "",
786 "`(\A|[\n;]+)/\*[^*]*\*+(?:[^/*][^*]*\*+)*/`" => "$1",
787 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n"
788 );
789
790 global $wp_styles;
791 if ( empty( $wp_styles->queue ) ) {
792 return;
793 }
794
795 // Make each of the scripts inline
796 foreach ( $wp_styles->queue as $handle ) {
797 if ( $handle === 'siteorigin-css-inspector' || $handle === 'dashicons' ) {
798 continue;
799 }
800 $style = $wp_styles->registered[ $handle ];
801 if ( empty( $style->src ) || substr( $style->src, 0, 4 ) !== 'http' ) {
802 continue;
803 }
804 $response = wp_remote_get( $style->src );
805 if ( is_wp_error( $response ) || $response['response']['code'] !== 200 || empty( $response['body'] ) ) {
806 continue;
807 }
808
809 $css = $response['body'];
810 $css = preg_replace( array_keys( $regex ), $regex, $css );
811
812 ?>
813 <script type="text/css" class="socss-theme-styles"
814 id="socss-inlined-style-<?php echo sanitize_html_class( $handle ) ?>">
815 <?php echo strip_tags( $css ); ?>
816 </script>
817 <?php
818 }
819 }
820
821 function disable_ngg_resource_manager() {
822 if ( ! current_user_can( 'edit_theme_options' ) ) {
823 return;
824 }
825
826 //The NextGen Gallery plugin does some weird interfering with the output buffer.
827 define( 'NGG_DISABLE_RESOURCE_MANAGER', true );
828 }
829
830 private function get_latest_revision_timestamp() {
831 $revisions = $this->get_custom_css_revisions( $this->theme );
832 if ( empty( $revisions ) ) {
833 return false;
834 }
835 krsort( $revisions );
836 $revision_times = array_keys( $revisions );
837
838 return $revision_times[0];
839 }
840 }
841
842 // Initialize the single
843 SiteOrigin_CSS::single();
844