PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.0.1
SiteOrigin CSS v1.0.1
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 11 years ago inc 11 years ago js 11 years ago lib 11 years ago tpl 11 years ago .gitmodules 11 years ago LICENSE 11 years ago readme.txt 11 years ago so-css.php 11 years ago
so-css.php
424 lines
1 <?php
2 /*
3 Plugin Name: SiteOrigin CSS
4 Description: An advanced CSS editor from SiteOrigin.
5 Version: 1.0.1
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.1');
17 define('SOCSS_JS_SUFFIX', '');
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') );
36 add_action( 'admin_enqueue_scripts', array($this, 'dequeue_admin_scripts'), 100 );
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 // Add in all the linting libs
146 wp_enqueue_script( 'codemirror-lint', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/lint/lint' . SOCSS_JS_SUFFIX . '.js', array( 'codemirror' ), '5.2.0' );
147 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' );
148 wp_enqueue_script( 'codemirror-lint-css-lib', plugin_dir_url(__FILE__) . 'js/csslint' . SOCSS_JS_SUFFIX . '.js', array(), '0.10.0' );
149
150 // The CodeMirror autocomplete library
151 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' );
152
153 // All the CodeMirror styles
154 wp_enqueue_style( 'codemirror', plugin_dir_url(__FILE__) . 'lib/codemirror/lib/codemirror.css', array(), '5.2.0' );
155 wp_enqueue_style( 'codemirror-theme-neat', plugin_dir_url(__FILE__) . 'lib/codemirror/theme/neat.css', array(), '5.2.0' );
156 wp_enqueue_style( 'codemirror-lint-css', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/lint/lint.css', array(), '5.2.0' );
157 wp_enqueue_style( 'codemirror-show-hint', plugin_dir_url(__FILE__) . 'lib/codemirror/addon/hint/show-hint.css', array( ), '5.2.0' );
158
159 // Enqueue the scripts for theme CSS processing
160 wp_enqueue_script( 'siteorigin-custom-css-parser', plugin_dir_url(__FILE__) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
161
162 // There are conflicts between CSS linting and the built in WordPress color picker, so use something else
163 wp_enqueue_style('siteorigin-custom-css-minicolors', plugin_dir_url(__FILE__) . 'lib/minicolors/jquery.minicolors.css', array(), '2.1.7' );
164 wp_enqueue_script('siteorigin-custom-css-minicolors', plugin_dir_url(__FILE__) . 'lib/minicolors/jquery.minicolors' . SOCSS_JS_SUFFIX . '.js', array('jquery'), '2.1.7' );
165
166 // We need Font Awesome
167 wp_enqueue_style( 'siteorigin-custom-css-font-awesome', plugin_dir_url(__FILE__) . 'lib/fontawesome/css/font-awesome.min.css', array( ), SOCSS_VERSION );
168
169 // All the custom SiteOrigin CSS stuff
170 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 );
171 wp_enqueue_style( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'css/admin.css', array( ), SOCSS_VERSION );
172
173 wp_localize_script( 'siteorigin-custom-css', 'socssOptions', array(
174 'themeCSS' => SiteOrigin_CSS::single()->get_theme_css(),
175 'homeURL' => add_query_arg( 'so_css_preview', '1', site_url() ),
176 'snippets' => $this->get_snippets(),
177
178 'propertyControllers' => apply_filters( 'siteorigin_css_property_controllers', $this->get_property_controllers() ),
179
180 'loc' => array(
181 'unchanged' => __('Unchanged', 'so-css'),
182 'select' => __('Select', 'so-css'),
183 'select_image' => __('Select Image', 'so-css'),
184 'leave' => __('Are you sure you want to leave without saving?', 'so-css'),
185 )
186 ) );
187
188 // This is for the templates required by the CSS editor
189 add_action( 'admin_footer', array($this, 'action_admin_footer') );
190 }
191
192 /**
193 * @param $page
194 */
195 function dequeue_admin_scripts( $page ) {
196 if( $page != 'appearance_page_so_custom_css' ) return;
197
198 // Dequeue the core WordPress color picker on the custom CSS page.
199 // This script causes conflicts and other plugins seem to be enqueueing it on the SO CSS admin page.
200 wp_dequeue_script('wp-color-picker');
201 wp_dequeue_style('wp-color-picker');
202 }
203
204 /**
205 * Get all the available property controllers
206 */
207 function get_property_controllers() {
208 return include plugin_dir_path(__FILE__) . 'inc/controller-config.php';
209 }
210
211 /**
212 * Display the templates for the CSS Editor
213 */
214 function action_admin_footer(){
215 include plugin_dir_path( __FILE__ ) . 'tpl/js-templates.php';
216 }
217
218 function display_admin_page(){
219 $theme = basename( get_template_directory() );
220
221 $custom_css = get_option( 'siteorigin_custom_css[' . $theme . ']', '' );
222 $custom_css_revisions = get_option('siteorigin_custom_css_revisions[' . $theme . ']');
223
224 if(!empty($_GET['theme']) && $_GET['theme'] == $theme && !empty($_GET['time']) && !empty($custom_css_revisions[$_GET['time']])) {
225 $custom_css = $custom_css_revisions[$_GET['time']];
226 $revision = true;
227 }
228
229 include plugin_dir_path(__FILE__).'/tpl/page.php';
230 }
231
232 /**
233 *
234 */
235 function admin_action_hide_getting_started(){
236 if( !isset($_GET['_wpnonce']) || !wp_verify_nonce( $_GET['_wpnonce'], 'hide' ) ) return;
237
238 $user = wp_get_current_user();
239 if( !empty($user) ) {
240 update_user_meta( $user->ID, 'socss_hide_gs', true );
241 }
242 }
243
244 /**
245 * Add one or more paths to the registered snippet paths
246 *
247 * @param string|array $path
248 */
249 function register_snippet_path( $path ){
250 $this->snippet_paths = array_merge( $this->snippet_paths, (array) $path );
251 }
252
253 /**
254 * Get all the available snippets
255 *
256 * @return array|bool
257 */
258 function get_snippets(){
259 // Get the snippet paths
260 $snippet_paths = apply_filters( 'siteorigin_css_snippet_paths', $this->snippet_paths );
261 if( empty($snippet_paths) ) return array();
262
263 static $snippets = array();
264 if( !empty($snippets) ) return $snippets;
265
266 if( !WP_Filesystem() ) return false;
267 global $wp_filesystem;
268 foreach( $snippet_paths as $path ) {
269 foreach( glob($path . '/*.css') as $css_file ) {
270 $data = get_file_data( $css_file, array(
271 'Name' => 'Name',
272 'Description' => 'Description',
273 ) );
274
275 // Get the CSS and strip out the first header
276 $css = $wp_filesystem->get_contents( $css_file );
277 $css = preg_replace('!/\*.*?\*/!s', '', $css, 1);
278
279 $snippets[] = wp_parse_args( $data, array(
280 'css' => str_replace( "\t", ' ', trim($css) ),
281 ) );
282 }
283 }
284
285 usort($snippets, array( $this, 'sort_snippet_callback' ) );
286 return $snippets;
287 }
288
289 /**
290 * Sort snippets by name.
291 *
292 * @param $a
293 * @param $b
294 *
295 * @return int
296 */
297 static function sort_snippet_callback( $a, $b ){
298 return $a['Name'] > $b['Name'] ? 1 : -1;
299 }
300
301 /**
302 * A very simple CSS sanitization function.
303 *
304 * @param $css
305 *
306 * @return string
307 */
308 static function sanitize_css( $css ){
309 return trim( strip_tags( $css ) );
310 }
311
312 /**
313 * Get all the available theme CSS
314 */
315 function get_theme_css(){
316 $css = '';
317 if( file_exists( get_template_directory() . '/style.css' ) ) {
318 $css .= file_get_contents( get_template_directory() . '/style.css' );
319 }
320
321 if( is_child_theme() ) {
322 $css .= file_get_contents( get_stylesheet_directory() . '/style.css' );
323 }
324
325 // Remove all CSS comments
326 $regex = array(
327 "`^([\t\s]+)`ism"=>'',
328 "`^\/\*(.+?)\*\/`ism"=>"",
329 "`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
330 "`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
331 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
332 );
333 $css = preg_replace( array_keys($regex), $regex, $css );
334 $css = preg_replace('/\s+/', ' ', $css);
335
336 return $css;
337 }
338
339 /**
340 * Get the editor description
341 *
342 * @return string
343 */
344 static function editor_description(){
345 $theme = wp_get_theme();
346 return sprintf( __( 'Changes apply to %s and its child themes', 'so-css' ), $theme->get('Name') );
347 }
348
349 function enqueue_inspector_scripts(){
350 if( !current_user_can('edit_theme_options') ) return;
351
352 wp_enqueue_style( 'dashicons' );
353
354 wp_enqueue_script( 'siteorigin-custom-css-parser', plugin_dir_url(__FILE__) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
355
356 wp_enqueue_script('siteorigin-css-sizes', plugin_dir_url(__FILE__) . 'js/jquery.sizes' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), '0.33' );
357 wp_enqueue_script('siteorigin-css-specificity', plugin_dir_url(__FILE__) . 'js/specificity' . SOCSS_JS_SUFFIX . '.js', array( ) );
358 wp_enqueue_script('siteorigin-css-inspector', plugin_dir_url(__FILE__) . 'js/inspector' . SOCSS_JS_SUFFIX . '.js', array( 'jquery', 'underscore', 'backbone' ), SOCSS_VERSION, true );
359 wp_enqueue_style('siteorigin-css-inspector', plugin_dir_url(__FILE__) . 'css/inspector.css', array( ), SOCSS_VERSION );
360
361 wp_localize_script('siteorigin-css-inspector', 'socssOptions', array(
362
363 ) );
364 }
365
366 function inspector_templates(){
367 if( !current_user_can('edit_theme_options') ) return;
368
369 include plugin_dir_path( __FILE__ ) . 'tpl/inspector-templates.php';
370 }
371
372 /**
373 * Change the stylesheets to all be inline
374 */
375 function inline_inspector_scripts(){
376 if( !current_user_can('edit_theme_options') ) return;
377
378 $regex = array(
379 "`^([\t\s]+)`ism"=>'',
380 "`^\/\*(.+?)\*\/`ism"=>"",
381 "`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
382 "`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
383 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
384 );
385
386 $styles = wp_styles();
387 foreach( $styles->queue as $handle ) {
388 if( $handle === 'siteorigin-css-inspector' || $handle === 'dashicons' ) continue;
389 $style = $styles->registered[$handle];
390 if( empty($style->src) || substr($style->src, 0, 4) !== 'http' ) continue;
391 $response = wp_remote_get( $style->src );
392 if( is_wp_error($response) || $response['response']['code'] !== 200 || empty($response['body']) ) continue;
393
394 $css = $response['body'];
395 $css = preg_replace( array_keys($regex), $regex, $css );
396
397 ?>
398 <script type="text/css" class="socss-theme-styles" id="socss-inlined-style-<?php echo sanitize_html_class( $handle ) ?>">
399 <?php echo strip_tags( $css ) ?>
400 </script>
401 <?php
402 }
403 }
404
405 /**
406 * Get a URL to tweet out the changes
407 */
408 function get_tweet_url(){
409 $tweet = __('I changed my site design using @SiteOrigin CSS (http://siteorigin.com/css/). What do you think?', 'so-css');
410 $tweet .= ' ';
411 $tweet .= get_site_url();
412
413 return add_query_arg(
414 'text',
415 urlencode($tweet),
416 'https://twitter.com/intent/tweet'
417 );
418
419
420 }
421 }
422
423 // Initialize the single
424 SiteOrigin_CSS::single();