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