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