PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.1.5
SiteOrigin CSS v1.1.5
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 8 years ago inc 9 years ago js 8 years ago lang 8 years ago lib 8 years ago tpl 9 years ago LICENSE 11 years ago readme.txt 8 years ago so-css.php 8 years ago
so-css.php
492 lines
1 <?php
2 /*
3 Plugin Name: SiteOrigin CSS
4 Description: An advanced CSS editor from SiteOrigin.
5 Version: 1.1.5
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.1.5');
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 add_action( 'wp_head', array($this, 'action_wp_head'), 20 );
34
35 // All the admin actions
36 add_action( 'admin_menu', array($this, 'action_admin_menu') );
37 add_action( 'admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'), 20 );
38 add_action( 'admin_enqueue_scripts', array($this, 'dequeue_admin_scripts'), 19 );
39 add_action( 'load-appearance_page_so_custom_css', array($this, 'add_help_tab') );
40 add_action( 'admin_footer', array($this, 'action_admin_footer') );
41
42 // Add the action links.
43 add_action( 'plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links') );
44
45 // The request to hide the getting started video
46 add_action( 'wp_ajax_socss_hide_getting_started', array( $this, 'admin_action_hide_getting_started' ) );
47
48 if( isset( $_GET['so_css_preview'] ) && !is_admin() ) {
49
50 add_action( 'plugins_loaded', array($this, 'disable_ngg_resource_manager') );
51 add_filter( 'show_admin_bar', '__return_false' );
52 add_filter( 'wp_enqueue_scripts', array($this, 'enqueue_inspector_scripts') );
53 add_filter( 'wp_footer', array($this, 'inspector_templates') );
54
55 // We'll be grabbing all the enqueued scripts and outputting them
56 add_action( 'wp_enqueue_scripts', array($this, 'inline_inspector_scripts'), 100 );
57 }
58 }
59
60 /**
61 * Get a singleton of the SiteOrigin CSS.
62 *
63 * @return SiteOrigin_CSS
64 */
65 static function single(){
66 static $single;
67
68 if( empty($single) ) {
69 $single = new SiteOrigin_CSS();
70 }
71
72 return $single;
73 }
74
75 /**
76 * Display the custom CSS in the header.
77 */
78 function action_wp_head() {
79 $upload_dir = wp_upload_dir();
80 $upload_dir_path = $upload_dir['basedir'] . '/so-css/';
81
82 $css_file_name = 'so-css-' . $this->theme;
83 $css_file_path = $upload_dir_path . $css_file_name . '.css';
84
85 if ( empty( $_GET['so_css_preview'] ) && ! is_admin() && file_exists( $css_file_path ) ) {
86 wp_enqueue_style(
87 'so-css-' . $this->theme,
88 set_url_scheme( $upload_dir['baseurl'] . '/so-css/' . $css_file_name . '.css' ),
89 array(),
90 $this->get_latest_revision_timestamp()
91 );
92 } else {
93 $custom_css = get_option( 'siteorigin_custom_css[' . $this->theme . ']', '' );
94 // We just need to enqueue a dummy style
95 if ( ! empty( $custom_css ) ) {
96 echo "<style id='" . sanitize_html_class($this->theme) . "-custom-css' class='siteorigin-custom-css' type='text/css'>\n";
97 echo self::sanitize_css( $custom_css ) . "\n";
98 echo "</style>\n";
99 }
100 }
101 }
102
103 function set_plugin_textdomain(){
104 load_plugin_textdomain( 'so-css', false, plugin_dir_path( __FILE__ ) . 'lang/' );
105 }
106
107 /**
108 * Action to run on the admin action.
109 */
110 function action_admin_menu(){
111 add_theme_page( __( 'Custom CSS', 'so-css' ), __( 'Custom CSS', 'so-css' ), 'edit_theme_options', 'so_custom_css', array( $this, 'display_admin_page' ) );
112
113 if ( current_user_can('edit_theme_options') && isset( $_POST['siteorigin_custom_css_save'] ) ) {
114 check_admin_referer( 'custom_css', '_sononce' );
115
116 // Sanitize CSS input. Should keep most tags, apart from script and style tags.
117 $custom_css = self::sanitize_css( filter_input(INPUT_POST, 'custom_css' ) );
118
119 $current = get_option('siteorigin_custom_css[' . $this->theme . ']');
120 if( $current === false ) {
121 add_option( 'siteorigin_custom_css[' . $this->theme . ']', $custom_css , '', 'no' );
122 }
123 else {
124 update_option( 'siteorigin_custom_css[' . $this->theme . ']', $custom_css );
125 }
126
127 // If this has changed, then add a revision.
128 if ( $current != $custom_css ) {
129 $revisions = get_option( 'siteorigin_custom_css_revisions[' . $this->theme . ']' );
130 if ( empty( $revisions ) ) {
131 add_option( 'siteorigin_custom_css_revisions[' . $this->theme . ']', array(), '', 'no' );
132 $revisions = array();
133 }
134 $revisions[ time() ] = $custom_css;
135
136 // Sort the revisions and cut off any old ones.
137 krsort($revisions);
138 $revisions = array_slice($revisions, 0, 15, true);
139
140 update_option( 'siteorigin_custom_css_revisions[' . $this->theme . ']', $revisions );
141
142 if( WP_Filesystem() ) {
143 global $wp_filesystem;
144 $upload_dir = wp_upload_dir();
145 $upload_dir_path = $upload_dir['basedir'] . '/so-css/';
146
147 if ( ! $wp_filesystem->is_dir( $upload_dir_path ) ) {
148 $wp_filesystem->mkdir( $upload_dir_path );
149 }
150
151 $css_file_name = 'so-css-' . $this->theme;
152 $css_file_path = $upload_dir_path . $css_file_name . '.css';
153
154 if ( file_exists( $css_file_path ) ) {
155 $wp_filesystem->delete( $css_file_path );
156 }
157
158 $wp_filesystem->put_contents(
159 $css_file_path,
160 $custom_css
161 );
162
163 }
164 }
165 }
166 }
167
168 /**
169 * Display the help tab
170 */
171 function add_help_tab(){
172 $screen = get_current_screen();
173 $screen->add_help_tab( array(
174 'id' => 'custom-css',
175 'title' => __( 'Custom CSS', 'so-css' ),
176 'content' => '<p>'
177 . sprintf( __( "SiteOrigin CSS adds any custom CSS you enter here into your site's header. ", 'so-css' ) )
178 . __( "These changes will persist across updates so it's best to make all your changes here. ", 'so-css' )
179 . '</p>'
180 ) );
181 }
182
183 function enqueue_admin_scripts( $page ){
184 if( $page != 'appearance_page_so_custom_css' ) return;
185
186 // Core WordPress stuff that we use
187 wp_enqueue_media();
188
189 // Enqueue the codemirror scripts. Call Underscore and Backbone dependencies so they're enqueued first to prevent conflicts.
190 wp_enqueue_script( 'codemirror', plugin_dir_url(__FILE__) . 'lib/codemirror/lib/codemirror' . SOCSS_JS_SUFFIX . '.js', array( 'underscore', 'backbone' ), '5.2.0' );
191 wp_enqueue_script( 'codemirror-mode-css', plugin_dir_url(__FILE__) . 'lib/codemirror/mode/css/css' . SOCSS_JS_SUFFIX . '.js', array(), '5.2.0' );
192
193 // Add in all the linting libs
194 wp_enqueue_script( 'codemirror-lint', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/lint/lint' . SOCSS_JS_SUFFIX . '.js', array( 'codemirror' ), '5.2.0' );
195 wp_enqueue_script( 'codemirror-lint-css', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/lint/css-lint' . SOCSS_JS_SUFFIX . '.js', array( 'codemirror', 'codemirror-lint-css-lib' ), '5.2.0' );
196 wp_enqueue_script( 'codemirror-lint-css-lib', plugin_dir_url(__FILE__) . 'js/csslint' . SOCSS_JS_SUFFIX . '.js', array(), '0.10.0' );
197
198 // The CodeMirror autocomplete library
199 wp_enqueue_script( 'codemirror-show-hint', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/hint/show-hint' . SOCSS_JS_SUFFIX . '.js', array( 'codemirror' ), '5.2.0' );
200
201 // All the CodeMirror styles
202 wp_enqueue_style( 'codemirror', plugin_dir_url(__FILE__) . 'lib/codemirror/lib/codemirror.css', array(), '5.2.0' );
203 wp_enqueue_style( 'codemirror-theme-neat', plugin_dir_url(__FILE__) . 'lib/codemirror/theme/neat.css', array(), '5.2.0' );
204 wp_enqueue_style( 'codemirror-lint-css', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/lint/lint.css', array(), '5.2.0' );
205 wp_enqueue_style( 'codemirror-show-hint', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/hint/show-hint.css', array( ), '5.2.0' );
206
207 // Enqueue the scripts for theme CSS processing
208 wp_enqueue_script( 'siteorigin-css-parser-lib', plugin_dir_url(__FILE__) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
209
210 // There are conflicts between CSS linting and the built in WordPress color picker, so use something else
211 wp_enqueue_style('siteorigin-custom-css-minicolors', plugin_dir_url(__FILE__) . 'lib/minicolors/jquery.minicolors.css', array(), '2.1.7' );
212 wp_enqueue_script('siteorigin-custom-css-minicolors', plugin_dir_url(__FILE__) . 'lib/minicolors/jquery.minicolors' . SOCSS_JS_SUFFIX . '.js', array('jquery'), '2.1.7' );
213
214 // We need Font Awesome
215 wp_enqueue_style( 'siteorigin-custom-css-font-awesome', plugin_dir_url(__FILE__) . 'lib/fontawesome/css/font-awesome.min.css', array( ), SOCSS_VERSION );
216
217 // URI parsing for preview navigation
218 wp_enqueue_script( 'siteorigin-uri', plugin_dir_url(__FILE__) . 'js/URI' . SOCSS_JS_SUFFIX . '.js', array( ), SOCSS_VERSION, true );
219
220 // All the custom SiteOrigin CSS stuff
221 wp_enqueue_script( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'js/editor' . SOCSS_JS_SUFFIX . '.js', array( 'jquery', 'underscore', 'backbone', 'siteorigin-css-parser-lib', 'codemirror' ), SOCSS_VERSION, true );
222 wp_enqueue_style( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'css/admin.css', array( ), SOCSS_VERSION );
223
224 wp_localize_script( 'siteorigin-custom-css', 'socssOptions', array(
225 'themeCSS' => SiteOrigin_CSS::single()->get_theme_css(),
226 // Pretty confusing, but it seems we should be using `home_url` and NOT `site_url`
227 // as described here => https://wordpress.stackexchange.com/a/50605
228 'homeURL' => add_query_arg( 'so_css_preview', '1', home_url() ),
229 'snippets' => $this->get_snippets(),
230
231 'propertyControllers' => apply_filters( 'siteorigin_css_property_controllers', $this->get_property_controllers() ),
232
233 'loc' => array(
234 'unchanged' => __('Unchanged', 'so-css'),
235 'select' => __('Select', 'so-css'),
236 'select_image' => __('Select Image', 'so-css'),
237 'leave' => __('Are you sure you want to leave without saving?', 'so-css'),
238 )
239 ) );
240
241 // This is for the templates required by the CSS editor
242 add_action( 'admin_footer', array($this, 'action_admin_footer') );
243 }
244
245 /**
246 * @param $page
247 */
248 function dequeue_admin_scripts( $page ) {
249 if( $page != 'appearance_page_so_custom_css' ) return;
250
251 // Dequeue the core WordPress color picker on the custom CSS page.
252 // This script causes conflicts and other plugins seem to be enqueueing it on the SO CSS admin page.
253 wp_dequeue_script('wp-color-picker');
254 wp_dequeue_style('wp-color-picker');
255 }
256
257 /**
258 * Get all the available property controllers
259 */
260 function get_property_controllers() {
261 return include plugin_dir_path( __FILE__ ) . 'inc/controller-config.php';
262 }
263
264 /**
265 * Display the templates for the CSS Editor
266 */
267 function action_admin_footer(){
268 include plugin_dir_path( __FILE__ ) . 'tpl/js-templates.php';
269 }
270
271 function plugin_action_links( $links ){
272 if( isset($links['edit']) ) unset( $links['edit'] );
273 $links['css_editor'] = '<a href="' . admin_url('themes.php?page=so_custom_css') . '">'.__('CSS Editor', 'so-css').'</a>';
274 $links['support'] = '<a href="https://siteorigin.com/thread/" target="_blank">'.__('Support', 'so-css').'</a>';
275 return $links;
276 }
277
278 function display_admin_page(){
279 $theme = basename( get_template_directory() );
280
281 $custom_css = get_option( 'siteorigin_custom_css[' . $theme . ']', '' );
282 $custom_css_revisions = get_option('siteorigin_custom_css_revisions[' . $theme . ']');
283
284 if( !empty( $_GET['theme'] ) && $_GET['theme'] == $theme && !empty( $_GET['time'] ) && !empty( $custom_css_revisions[$_GET['time']] ) ) {
285 $custom_css = $custom_css_revisions[$_GET['time']];
286 $revision = true;
287 }
288
289 include plugin_dir_path( __FILE__ ) . 'tpl/page.php';
290 }
291
292
293 function display_teaser(){
294 return apply_filters( 'siteorigin_premium_upgrade_teaser', true ) &&
295 ! defined( 'SITEORIGIN_PREMIUM_VERSION' );
296 }
297
298 /**
299 *
300 */
301 function admin_action_hide_getting_started(){
302 if( !isset($_GET['_wpnonce']) || !wp_verify_nonce( $_GET['_wpnonce'], 'hide' ) ) return;
303
304 $user = wp_get_current_user();
305 if( !empty( $user ) ) {
306 update_user_meta( $user->ID, 'socss_hide_gs', true );
307 }
308 }
309
310 /**
311 * Add one or more paths to the registered snippet paths
312 *
313 * @param string|array $path
314 */
315 function register_snippet_path( $path ){
316 $this->snippet_paths = array_merge( $this->snippet_paths, (array) $path );
317 }
318
319 /**
320 * Get all the available snippets
321 *
322 * @return array|bool
323 */
324 function get_snippets(){
325 // Get the snippet paths
326 $snippet_paths = apply_filters( 'siteorigin_css_snippet_paths', $this->snippet_paths );
327 if( empty($snippet_paths) ) return array();
328
329 static $snippets = array();
330 if( !empty($snippets) ) return $snippets;
331
332 if( !WP_Filesystem() ) return false;
333 global $wp_filesystem;
334 foreach( $snippet_paths as $path ) {
335 foreach( glob($path . '/*.css') as $css_file ) {
336 $data = get_file_data( $css_file, array(
337 'Name' => 'Name',
338 'Description' => 'Description',
339 ) );
340
341 // Get the CSS and strip out the first header
342 $css = $wp_filesystem->get_contents( $css_file );
343 $css = preg_replace('!/\*.*?\*/!s', '', $css, 1);
344
345 $snippets[] = wp_parse_args( $data, array(
346 'css' => str_replace( "\t", ' ', trim($css) ),
347 ) );
348 }
349 }
350
351 usort($snippets, array( $this, 'sort_snippet_callback' ) );
352 return $snippets;
353 }
354
355 /**
356 * Sort snippets by name.
357 *
358 * @param $a
359 * @param $b
360 *
361 * @return int
362 */
363 static function sort_snippet_callback( $a, $b ){
364 return $a['Name'] > $b['Name'] ? 1 : -1;
365 }
366
367 /**
368 * A very simple CSS sanitization function.
369 *
370 * @param $css
371 *
372 * @return string
373 */
374 static function sanitize_css( $css ){
375 return trim( strip_tags( $css ) );
376 }
377
378 /**
379 * Get all the available theme CSS
380 */
381 function get_theme_css(){
382 $css = '';
383 if( file_exists( get_template_directory() . '/style.css' ) ) {
384 $css .= file_get_contents( get_template_directory() . '/style.css' );
385 }
386
387 if( is_child_theme() ) {
388 $css .= file_get_contents( get_stylesheet_directory() . '/style.css' );
389 }
390
391 // Remove all CSS comments
392 $regex = array(
393 "`^([\t\s]+)`ism"=>'',
394 "`^\/\*(.+?)\*\/`ism"=>"",
395 "`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
396 "`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
397 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
398 );
399 $css = preg_replace( array_keys($regex), $regex, $css );
400 $css = preg_replace('/\s+/', ' ', $css);
401
402 return $css;
403 }
404
405 /**
406 * Get the editor description
407 *
408 * @return string
409 */
410 static function editor_description(){
411 $theme = wp_get_theme();
412 return sprintf( __( 'Changes apply to %s and its child themes', 'so-css' ), $theme->get('Name') );
413 }
414
415 function enqueue_inspector_scripts(){
416 if( !current_user_can('edit_theme_options') ) return;
417
418 wp_enqueue_style( 'dashicons' );
419
420 wp_enqueue_script( 'siteorigin-css-parser-lib', plugin_dir_url(__FILE__) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
421
422 wp_enqueue_script('siteorigin-css-sizes', plugin_dir_url(__FILE__) . 'js/jquery.sizes' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), '0.33' );
423 wp_enqueue_script('siteorigin-css-specificity', plugin_dir_url(__FILE__) . 'js/specificity' . SOCSS_JS_SUFFIX . '.js', array( ) );
424 wp_enqueue_script('siteorigin-css-inspector', plugin_dir_url(__FILE__) . 'js/inspector' . SOCSS_JS_SUFFIX . '.js', array( 'jquery', 'underscore', 'backbone' ), SOCSS_VERSION, true );
425 wp_enqueue_style('siteorigin-css-inspector', plugin_dir_url(__FILE__) . 'css/inspector.css', array( ), SOCSS_VERSION );
426
427 wp_localize_script('siteorigin-css-inspector', 'socssOptions', array(
428
429 ) );
430 }
431
432 function inspector_templates(){
433 if( !current_user_can('edit_theme_options') ) return;
434
435 include plugin_dir_path( __FILE__ ) . 'tpl/inspector-templates.php';
436 }
437
438 /**
439 * Change the stylesheets to all be inline
440 */
441 function inline_inspector_scripts(){
442 if( !current_user_can('edit_theme_options') ) return;
443
444 $regex = array(
445 "`^([\t\s]+)`ism"=>'',
446 "`^\/\*(.+?)\*\/`ism"=>"",
447 "`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
448 "`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
449 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
450 );
451
452 global $wp_styles;
453 if( empty($wp_styles->queue) ) return;
454
455 // Make each of the scripts inline
456 foreach( $wp_styles->queue as $handle ) {
457 if( $handle === 'siteorigin-css-inspector' || $handle === 'dashicons' ) continue;
458 $style = $wp_styles->registered[$handle];
459 if( empty($style->src) || substr($style->src, 0, 4) !== 'http' ) continue;
460 $response = wp_remote_get( $style->src );
461 if( is_wp_error($response) || $response['response']['code'] !== 200 || empty($response['body']) ) continue;
462
463 $css = $response['body'];
464 $css = preg_replace( array_keys($regex), $regex, $css );
465
466 ?>
467 <script type="text/css" class="socss-theme-styles" id="socss-inlined-style-<?php echo sanitize_html_class( $handle ) ?>">
468 <?php echo strip_tags( $css ) ?>
469 </script>
470 <?php
471 }
472 }
473
474 function disable_ngg_resource_manager() {
475 if( !current_user_can('edit_theme_options') ) return;
476
477 //The NextGen Gallery plugin does some weird interfering with the output buffer.
478 define('NGG_DISABLE_RESOURCE_MANAGER', true);
479 }
480
481 private function get_latest_revision_timestamp() {
482 $revisions = get_option( 'siteorigin_custom_css_revisions[' . $this->theme . ']' );
483 krsort( $revisions );
484 $revision_times = array_keys( $revisions );
485
486 return $revision_times[0];
487 }
488 }
489
490 // Initialize the single
491 SiteOrigin_CSS::single();
492