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