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