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