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