PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.3.2
SiteOrigin CSS v1.3.2
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 4 years ago inc 5 years ago js 5 years ago lang 4 years ago lib 6 years ago tpl 5 years ago LICENSE 11 years ago readme.txt 4 years ago so-css.php 4 years ago
so-css.php
803 lines
1 <?php
2 /*
3 Plugin Name: SiteOrigin CSS
4 Description: An advanced CSS editor from SiteOrigin.
5 Version: 1.3.2
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 Text Domain: so-css
12 */
13
14 // Handle the legacy CSS editor that came with SiteOrigin themes
15 include plugin_dir_path( __FILE__ ) . 'inc/legacy.php';
16
17 define( 'SOCSS_VERSION', '1.3.2' );
18 define( 'SOCSS_JS_SUFFIX', '.min' );
19
20 /**
21 * Class SiteOrigin_CSS The main class for the SiteOrigin CSS Editor
22 */
23 class SiteOrigin_CSS {
24 private $theme;
25 private $snippet_paths;
26
27 function __construct() {
28 $this->theme = basename( get_template_directory() );
29 $this->snippet_paths = array();
30
31 // Main header actions
32 add_action( 'plugins_loaded', array( $this, 'set_plugin_textdomain' ) );
33
34 // Priority 20 is necessary to ensure our CSS takes precedence.
35 add_action( 'wp_head', array( $this, 'enqueue_css' ), 20 );
36
37 // All the admin actions
38 add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
39 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ), 20 );
40 add_action( 'admin_enqueue_scripts', array( $this, 'dequeue_admin_scripts' ), 19 );
41 add_action( 'load-appearance_page_so_custom_css', array( $this, 'add_help_tab' ) );
42
43 // Add the action links.
44 add_action( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
45
46 // The request to hide the getting started video
47 add_action( 'wp_ajax_socss_hide_getting_started', array( $this, 'admin_action_hide_getting_started' ) );
48
49 add_action( 'wp_ajax_socss_get_post_css', array( $this, 'admin_action_get_post_css' ) );
50 add_action( 'wp_ajax_socss_get_revisions_list', array( $this, 'admin_action_get_revisions_list' ) );
51
52 if ( ! is_admin() ) {
53 if( isset( $_GET['so_css_preview'] ) ) {
54
55 add_action( 'plugins_loaded', array($this, 'disable_ngg_resource_manager') );
56 add_filter( 'show_admin_bar', '__return_false' );
57 add_filter( 'wp_enqueue_scripts', array($this, 'enqueue_inspector_scripts') );
58 add_filter( 'wp_footer', array($this, 'inspector_templates') );
59
60 // We'll be grabbing all the enqueued scripts and outputting them
61 add_action( 'wp_enqueue_scripts', array($this, 'inline_inspector_scripts'), 100 );
62 }
63 }
64 }
65
66 /**
67 * Get a singleton of the SiteOrigin CSS.
68 *
69 * @return SiteOrigin_CSS
70 */
71 static function single() {
72 static $single;
73
74 if ( empty( $single ) ) {
75 $single = new SiteOrigin_CSS();
76 }
77
78 return $single;
79 }
80
81 /**
82 * Retrieve the current custom CSS for a given theme and post id combination.
83 *
84 * @param $theme string The name of the theme for which to retrieve custom CSS.
85 * @param $post_id int The ID of the specific post for which to retrieve custom CSS.
86 *
87 * @return string The custom CSS for the theme and post ID combination.
88 */
89 function get_custom_css( $theme, $post_id = null ) {
90 $css_key = 'siteorigin_custom_css[' . $theme . ']';
91 if ( empty( $post_id ) ) {
92 return get_option( $css_key, '' );
93 }
94
95 return get_post_meta( $post_id, $css_key, true );
96 }
97
98 /**
99 * Save custom CSS for a given theme and post id combination.
100 *
101 * @param $custom_css string The custom CSS to save.
102 * @param $theme string The name of the theme for which to save custom CSS.
103 * @param $post_id int The ID of the specific post for which to save custom CSS.
104 *
105 * @return bool Whether or not saving the custom CSS was successful.
106 */
107 function save_custom_css( $custom_css, $theme, $post_id = null ) {
108 $css_key = 'siteorigin_custom_css[' . $theme . ']';
109 if ( empty( $post_id ) ) {
110 $current = get_option( $css_key );
111 if ( $current === false ) {
112 return add_option( $css_key, $custom_css, '', 'no' );
113 } else {
114 return update_option( $css_key, $custom_css );
115 }
116 }
117
118 if ( metadata_exists( 'post', $post_id, $css_key ) ) {
119 return update_post_meta( $post_id, $css_key, $custom_css );
120 }
121
122 return add_post_meta( $post_id, $css_key, $custom_css );
123 }
124
125 /**
126 * Save custom CSS for a given theme and post id combination to a file in the uploads directory to allow for caching.
127 *
128 * @param $custom_css
129 * @param $theme
130 * @param null $post_id
131 */
132 function save_custom_css_file( $custom_css, $theme, $post_id = null ) {
133 if ( WP_Filesystem() ) {
134 global $wp_filesystem;
135 $upload_dir = wp_upload_dir();
136 $upload_dir_path = $upload_dir['basedir'] . '/so-css/';
137
138 if ( ! $wp_filesystem->is_dir( $upload_dir_path ) ) {
139 $wp_filesystem->mkdir( $upload_dir_path );
140 }
141
142 $css_file_name = 'so-css-' . $theme . ( ! empty( $post_id ) ? '_' . $post_id : '' );
143 $css_file_path = $upload_dir_path . $css_file_name . '.css';
144
145 if ( file_exists( $css_file_path ) ) {
146 $wp_filesystem->delete( $css_file_path );
147 }
148
149 $wp_filesystem->put_contents(
150 $css_file_path,
151 $custom_css
152 );
153 }
154 }
155
156 /**
157 * Retrieve the previous revisions of custom CSS for a given theme and post id combination.
158 *
159 * @param $theme string The name of the theme for which to retrieve custom CSS revisions.
160 * @param $post_id int The ID of the specific post for which to retrieve custom CSS revisions.
161 *
162 * @return array The custom CSS revisions for the theme and post ID combination.
163 */
164 function get_custom_css_revisions( $theme, $post_id = null ) {
165 $css_key = 'siteorigin_custom_css_revisions[' . $theme . ']';
166 if ( empty( $post_id ) ) {
167 return get_option( $css_key, '' );
168 }
169
170 return get_post_meta( $post_id, $css_key, true );
171 }
172
173 /**
174 * Adds a custom CSS revision for a given theme and post id combination.
175 *
176 * @param $custom_css string The custom CSS to add as a revision.
177 * @param $theme string The name of the theme for which to save custom CSS.
178 * @param $post_id int The ID of the specific post for which to save custom CSS.
179 *
180 * @return bool Whether or not adding the custom CSS revision was successful.
181 */
182 function add_custom_css_revision( $custom_css, $theme, $post_id = null ) {
183 $revisions = $this->get_custom_css_revisions( $this->theme, $post_id );
184
185 $css_key = 'siteorigin_custom_css_revisions[' . $theme . ']';
186
187 if ( empty( $revisions ) ) {
188 $revisions = array();
189 if ( empty( $post_id ) ) {
190 add_option( $css_key, $revisions, '', 'no' );
191 } else {
192 add_post_meta( $post_id, $css_key, $revisions );
193 }
194 }
195 $revisions[ time() ] = $custom_css;
196
197 // Sort the revisions and cut off any old ones.
198 krsort( $revisions );
199 $revisions = array_slice( $revisions, 0, 15, true );
200
201 if ( empty( $post_id ) ) {
202 return update_option( $css_key, $revisions );
203 }
204
205 return update_post_meta( $post_id, $css_key, $revisions );
206 }
207
208 /**
209 * Enqueue or print inline CSS.
210 */
211 function enqueue_css() {
212
213 $this->enqueue_custom_css( $this->theme );
214
215 if ( is_singular() ) {
216 $this->enqueue_custom_css( $this->theme, get_the_ID() );
217 }
218
219 }
220
221 /**
222 * Enqueue the custom CSS for the given theme and post id combination.
223 *
224 * @param $theme string The name of the theme for which to enqueue custom CSS.
225 * @param $post_id int The ID of the specific post for which to enqueue custom CSS.
226 *
227 */
228 function enqueue_custom_css( $theme, $post_id = null ) {
229
230 $upload_dir = wp_upload_dir();
231 $upload_dir_path = $upload_dir['basedir'] . '/so-css/';
232
233 $css_id = $theme . ( ! empty( $post_id ) ? '_' . $post_id : '' );
234 $css_file_name = 'so-css-' . $css_id;
235 $css_file_path = $upload_dir_path . $css_file_name . '.css';
236
237 if ( empty( $_GET['so_css_preview'] ) && ! is_admin() && file_exists( $css_file_path ) ) {
238 wp_enqueue_style(
239 'so-css-' . $css_id,
240 set_url_scheme( $upload_dir['baseurl'] . '/so-css/' . $css_file_name . '.css' ),
241 array(),
242 $this->get_latest_revision_timestamp()
243 );
244 } else {
245 $custom_css = $this->get_custom_css( $theme, $post_id );
246 // We just need to enqueue a dummy style
247 if ( ! empty( $custom_css ) ) {
248 echo "<style id='" . sanitize_html_class( $css_id ) . "-custom-css' class='siteorigin-custom-css' type='text/css'>\n";
249 echo self::sanitize_css( $custom_css ) . "\n";
250 echo "</style>\n";
251 }
252 }
253 }
254
255 function set_plugin_textdomain() {
256 load_plugin_textdomain( 'so-css', false, plugin_dir_path( __FILE__ ) . 'lang/' );
257 }
258
259 /**
260 * Action to run on the admin action.
261 */
262 function action_admin_menu() {
263 add_theme_page( __( 'Custom CSS', 'so-css' ), __( 'Custom CSS', 'so-css' ), 'edit_theme_options', 'so_custom_css', array(
264 $this,
265 'display_admin_page'
266 ) );
267
268 if ( current_user_can( 'edit_theme_options' ) && isset( $_POST['siteorigin_custom_css'] ) ) {
269 check_admin_referer( 'custom_css', '_sononce' );
270
271 // Sanitize CSS input. Should keep most tags, apart from script and style tags.
272 $custom_css = self::sanitize_css( filter_input( INPUT_POST, 'siteorigin_custom_css' ) );
273 $socss_post_id = filter_input( INPUT_GET, 'socss_post_id', FILTER_VALIDATE_INT );
274
275 $current = $this->get_custom_css( $this->theme, $socss_post_id );
276 $this->save_custom_css( $custom_css, $this->theme, $socss_post_id );
277
278 // If this has changed, then add a revision.
279 if ( $current != $custom_css ) {
280 $this->add_custom_css_revision( $custom_css, $this->theme, $socss_post_id );
281
282 $this->save_custom_css_file( $custom_css, $this->theme, $socss_post_id );
283 }
284 }
285 }
286
287 /**
288 * Display the help tab
289 */
290 function add_help_tab() {
291 $screen = get_current_screen();
292 $screen->add_help_tab( array(
293 'id' => 'custom-css',
294 'title' => __( 'Custom CSS', 'so-css' ),
295 'content' => '<p>'
296 . sprintf( __( "SiteOrigin CSS adds any custom CSS you enter here into your site's header. ", 'so-css' ) )
297 . __( "These changes will persist across updates so it's best to make all your changes here. ", 'so-css' )
298 . '</p>'
299 ) );
300 }
301
302 function enqueue_admin_scripts( $page ) {
303 if ( $page != 'appearance_page_so_custom_css' ) {
304 return;
305 }
306
307 // Core WordPress stuff that we use
308 wp_enqueue_media();
309
310 global $wp_version;
311 if ( version_compare( $wp_version, '4.9', '>=' ) && wp_get_current_user()->syntax_highlighting ) {
312 wp_enqueue_code_editor( array(
313 'type' => 'css',
314 'codemirror' => array(
315 'lint' => true,
316 ),
317 )
318 );
319 } else {
320 $this->enqueue_fallback_codemirror();
321 }
322 wp_enqueue_style( 'socss-codemirror-theme-neat', plugin_dir_url( __FILE__ ) . 'lib/codemirror/theme/neat.css', array(), '5.2.0' );
323
324 // Enqueue the scripts for theme CSS processing
325 wp_enqueue_script( 'siteorigin-css-parser-lib', plugin_dir_url( __FILE__ ) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
326
327 // There are conflicts between CSS linting and the built in WordPress color picker, so use something else
328 wp_enqueue_style( 'siteorigin-custom-css-minicolors', plugin_dir_url( __FILE__ ) . 'lib/minicolors/jquery.minicolors.css', array(), '2.1.7' );
329 wp_enqueue_script( 'siteorigin-custom-css-minicolors', plugin_dir_url( __FILE__ ) . 'lib/minicolors/jquery.minicolors' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), '2.1.7' );
330
331 // We need Font Awesome
332 wp_enqueue_style( 'siteorigin-custom-css-font-awesome', plugin_dir_url( __FILE__ ) . 'lib/fontawesome/css/font-awesome.min.css', array(), SOCSS_VERSION );
333
334 // URI parsing for preview navigation
335 wp_enqueue_script( 'siteorigin-uri', plugin_dir_url( __FILE__ ) . 'js/URI' . SOCSS_JS_SUFFIX . '.js', array(), SOCSS_VERSION, true );
336
337 // All the custom SiteOrigin CSS stuff
338 wp_enqueue_script( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'js/editor' . SOCSS_JS_SUFFIX . '.js', array( 'jquery', 'underscore', 'backbone' ), SOCSS_VERSION, true );
339 wp_enqueue_style( 'siteorigin-custom-css', plugin_dir_url(__FILE__) . 'css/admin.css', array( ), SOCSS_VERSION );
340
341
342 // Pretty confusing, but it seems we should be using `home_url` and NOT `site_url`
343 // as described here => https://wordpress.stackexchange.com/a/50605
344 $init_url = home_url();
345
346 if ( ! empty( $socss_post_id ) && is_int( $socss_post_id ) ) {
347 $init_url = set_url_scheme( get_permalink( $socss_post_id ) );
348 }
349
350 $open_visual_editor = ! empty( $_REQUEST['open_visual_editor'] );
351
352 $home_url = add_query_arg( 'so_css_preview', '1', $init_url );
353
354 wp_localize_script( 'siteorigin-custom-css', 'socssOptions', array(
355 'homeURL' => $home_url,
356 'postCssUrlRoot' => wp_nonce_url( admin_url('admin-ajax.php?action=socss_get_post_css'), 'get_post_css' ),
357 'getRevisionsListAjaxUrl' => wp_nonce_url( admin_url('admin-ajax.php?action=socss_get_revisions_list'), 'get_revisions_list' ),
358 'openVisualEditor' => $open_visual_editor,
359
360 'propertyControllers' => apply_filters( 'siteorigin_css_property_controllers', $this->get_property_controllers() ),
361
362 'loc' => array(
363 'unchanged' => __( 'Unchanged', 'so-css' ),
364 'select' => __( 'Select', 'so-css' ),
365 'select_image' => __( 'Select Image', 'so-css' ),
366 'leave' => __( 'Are you sure you want to leave without saving?', 'so-css' ),
367 )
368 ) );
369
370 // This is for the templates required by the CSS editor. Ideally this would be out in the footer, but we need
371 // it earlier for dependent scripts.
372 include plugin_dir_path( __FILE__ ) . 'tpl/js-templates.php';
373 }
374
375 // Handles loading the fallback version of CodeMirror.
376 function enqueue_fallback_codemirror() {
377 // Enqueue the codemirror scripts. Call Underscore and Backbone dependencies so they're enqueued first to prevent conflicts.
378 wp_enqueue_script( 'socss-codemirror', plugin_dir_url( __FILE__ ) . 'lib/codemirror/lib/codemirror' . SOCSS_JS_SUFFIX . '.js', array( 'underscore', 'backbone' ), '5.2.0' );
379 wp_enqueue_script( 'socss-codemirror-mode-css', plugin_dir_url( __FILE__ ) . 'lib/codemirror/mode/css/css' . SOCSS_JS_SUFFIX . '.js', array(), '5.2.0' );
380
381 // Add in all the linting libs
382 wp_enqueue_script( 'socss-codemirror-lint', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/lint/lint' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.2.0' );
383 wp_enqueue_script( 'socss-codemirror-lint-css', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/lint/css-lint' . SOCSS_JS_SUFFIX . '.js', array(
384 'socss-codemirror',
385 'socss-codemirror-lint-css-lib'
386 ), '5.2.0' );
387 wp_enqueue_script( 'socss-codemirror-lint-css-lib', plugin_dir_url( __FILE__ ) . 'js/csslint' . SOCSS_JS_SUFFIX . '.js', array(), '0.10.0' );
388
389 // The CodeMirror autocomplete library
390 wp_enqueue_script( 'socss-codemirror-show-hint', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/hint/show-hint' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.2.0' );
391
392 // CodeMirror search and dialog addons
393 wp_enqueue_script( 'socss-codemirror-dialog', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/dialog/dialog' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.2.0' );
394
395 wp_enqueue_script( 'socss-codemirror-search', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/search' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror' ), '5.37.0' );
396 wp_enqueue_script( 'socss-codemirror-search-searchcursor', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/searchcursor' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
397 wp_enqueue_script( 'socss-codemirror-search-match-cursor', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/match-highlighter' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
398 wp_enqueue_script( 'socss-codemirror-search-matchesonscrollbar', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/matchesonscrollbar' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
399 wp_enqueue_script( 'socss-codemirror-scroll-annotatescrollbar', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/scroll/annotatescrollbar' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search', 'socss-codemirror-search-matchesonscrollbar' ), '5.37.0' );
400 wp_enqueue_script( 'socss-codemirror-jump-to-line', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/search/jump-to-line' . SOCSS_JS_SUFFIX . '.js', array( 'socss-codemirror', 'socss-codemirror-search' ), '5.37.0' );
401
402 // All the CodeMirror styles
403 wp_enqueue_style( 'socss-codemirror', plugin_dir_url( __FILE__ ) . 'lib/codemirror/lib/codemirror.css', array(), '5.2.0' );
404 wp_enqueue_style( 'socss-codemirror-lint-css', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/lint/lint.css', array(), '5.2.0' );
405 wp_enqueue_style( 'socss-codemirror-show-hint', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/hint/show-hint.css', array(), '5.2.0' );
406 wp_enqueue_style( 'socss-codemirror-dialog', plugin_dir_url( __FILE__ ) . 'lib/codemirror/addon/dialog/dialog.css', '5.2.0' );
407 wp_enqueue_style( 'socss-codemirror-search-matchesonscrollbar', plugin_dir_url(__FILE__ ) . 'lib/codemirror/addon/search/matchesonscrollbar.css', array(), '5.37.0' );
408 }
409
410 /**
411 * @param $page
412 */
413 function dequeue_admin_scripts( $page ) {
414 if ( $page != 'appearance_page_so_custom_css' ) {
415 return;
416 }
417
418 // Dequeue the core WordPress color picker on the custom CSS page.
419 // This script causes conflicts and other plugins seem to be enqueueing it on the SO CSS admin page.
420 wp_dequeue_script( 'wp-color-picker' );
421 wp_dequeue_style( 'wp-color-picker' );
422 }
423
424 /**
425 * Get all the available property controllers
426 */
427 function get_property_controllers() {
428 return include plugin_dir_path( __FILE__ ) . 'inc/controller-config.php';
429 }
430
431 function plugin_action_links( $links ) {
432 if ( isset( $links['edit'] ) ) {
433 unset( $links['edit'] );
434 }
435 $links['css_editor'] = '<a href="' . admin_url( 'themes.php?page=so_custom_css' ) . '">' . __( 'CSS Editor', 'so-css' ) . '</a>';
436 $links['support'] = '<a href="https://siteorigin.com/thread/" target="_blank">' . __( 'Support', 'so-css' ) . '</a>';
437 if ( apply_filters( 'siteorigin_premium_upgrade_teaser', true ) && ! defined( 'SITEORIGIN_PREMIUM_VERSION' ) ) {
438 $links['addons'] = '<a href="https://siteorigin.com/downloads/premium/?featured_addon=plugin/web-font-selector" style="color: #3db634" target="_blank" rel="noopener noreferrer">' . __( 'Addons', 'so-css' ) . '</a>';
439 }
440 return $links;
441 }
442
443 function display_admin_page() {
444
445 $socss_post_id = filter_input( INPUT_GET, 'socss_post_id', FILTER_VALIDATE_INT );
446 $theme = filter_input( INPUT_GET, 'theme' );
447 $time = filter_input( INPUT_GET, 'time', FILTER_VALIDATE_INT );
448
449 $page_title = __( 'SiteOrigin CSS', 'so-css' );
450 $theme_obj = wp_get_theme();
451 $theme_name = $theme_obj->get( 'Name' );
452 $editor_description = sprintf( __( 'Changes apply to %s and its child themes', 'so-css' ), $theme_name );
453 $save_button_label = __( 'Save CSS', 'so-css' );
454 $form_save_url = admin_url( 'themes.php?page=so_custom_css' );
455
456 if ( ! empty( $socss_post_id ) ) {
457 $selected_post = get_post( $socss_post_id );
458
459 $page_title = sprintf(
460 __( 'Editing CSS for: %s', 'so-css' ),
461 $selected_post->post_title
462 );
463
464 $editor_description = sprintf(
465 __( 'Changes apply to the %s %s when the current theme is %s or its child themes', 'so-css' ),
466 $selected_post->post_type,
467 $selected_post->post_title,
468 $theme_name
469 );
470 $post_type_obj = get_post_type_object( $selected_post->post_type );
471 $post_type_labels = $post_type_obj->labels;
472 $save_button_label = sprintf( __( 'Save %s CSS', 'so-css' ), $post_type_labels->singular_name );
473 $form_save_url = add_query_arg( 'socss_post_id', urlencode( $socss_post_id ), $form_save_url );
474 }
475 $custom_css = $this->get_custom_css( $this->theme, $socss_post_id );
476 $custom_css_revisions = $this->get_custom_css_revisions( $this->theme, $socss_post_id );
477 $current_revision = 0;
478
479 if ( ! empty( $theme ) && $theme == $this->theme && ! empty( $time ) && ! empty( $custom_css_revisions[ $time ] ) ) {
480 $current_revision = $time;
481 $custom_css = $custom_css_revisions[ $time ];
482 }
483
484 if ( ! empty ( $current_revision ) ) {
485 $save_button_label = __( 'Revert to this revision', 'so-css' );
486 }
487
488 if ( ! empty( $custom_css_revisions ) ) {
489 krsort( $custom_css_revisions );
490 }
491
492 $theme = basename( get_template_directory() );
493
494 include plugin_dir_path( __FILE__ ) . 'tpl/page.php';
495 }
496
497
498 function display_teaser() {
499 return apply_filters( 'siteorigin_premium_upgrade_teaser', true ) &&
500 ! defined( 'SITEORIGIN_PREMIUM_VERSION' );
501 }
502
503 /**
504 * Generates the url to edit the custom CSS for a post.
505 */
506 function get_edit_css_link( $post ) {
507 $url = admin_url( 'themes.php?page=so_custom_css' );
508 if ( ! is_int( $post ) ) {
509 $post = get_post( $post );
510 $post = $post->ID;
511 }
512
513 return empty( $post ) ? $url : add_query_arg( array(
514 'socss_post_id' => urlencode( $post ),
515 'open_visual_editor' => 1,
516 ), $url );
517 }
518 /**
519 *
520 */
521 function admin_action_hide_getting_started() {
522 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'hide' ) ) {
523 return;
524 }
525
526 $user = wp_get_current_user();
527 if ( ! empty( $user ) ) {
528 update_user_meta( $user->ID, 'socss_hide_gs', true );
529 }
530 }
531
532 /**
533 * Retrieves the post specific CSS for the supplied postId.
534 */
535 function admin_action_get_post_css() {
536 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'get_post_css' ) ) {
537 wp_die(
538 __( 'The supplied nonce is invalid.', 'siteorigin-panels' ),
539 __( 'Invalid nonce.', 'siteorigin-panels' ),
540 403
541 );
542 }
543
544 $post_id = filter_input( INPUT_GET, 'postId', FILTER_VALIDATE_INT );
545
546 $current = $this->get_custom_css( $this->theme, $post_id );
547
548 $url = empty( $post_id ) ? home_url() : set_url_scheme( get_permalink( $post_id ) );
549
550 wp_send_json( array( 'css' => empty( $current ) ? '' : $current, 'postUrl' => $url ) );
551 }
552
553 /**
554 * Retrieves the past revisions of post specific CSS for the supplied postId.
555 */
556 function admin_action_get_revisions_list() {
557 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'get_revisions_list' ) ) {
558 wp_die(
559 __( 'The supplied nonce is invalid.', 'siteorigin-panels' ),
560 __( 'Invalid nonce.', 'siteorigin-panels' ),
561 403
562 );
563 }
564
565 $post_id = filter_input( INPUT_GET, 'postId', FILTER_VALIDATE_INT );
566
567 $this->custom_css_revisions_list( $this->theme, $post_id );
568
569 wp_die();
570 }
571
572 function custom_css_revisions_list( $theme, $post_id = null, $current_revision = null ) {
573
574 $revisions = $this->get_custom_css_revisions( $theme, $post_id );
575
576 if ( is_array( $revisions ) && ! empty( $revisions ) ) {
577 $i = 0;
578 foreach ( $revisions as $time => $css ) {
579 $is_current = ( empty( $current_revision ) && $i == 0 ) || ( ! empty( $current_revision ) && $time == $current_revision );
580 $query_args = array( 'theme' => $theme, 'time' => $time, 'open_visual_editor' => false );
581 if ( ! empty( $post_id ) ) {
582 $query_args['socss_post_id'] = $post_id;
583 }
584 ?>
585 <li>
586 <?php if ( ! $is_current ) : ?>
587 <a href="<?php echo esc_url( add_query_arg( $query_args, admin_url( 'themes.php?page=so_custom_css' ) ) ) ?>"
588 class="load-css-revision">
589 <?php endif; ?>
590 <?php echo date('j F Y @ H:i:s', $time + get_option('gmt_offset') * 60 * 60) ?>
591 <?php if ( ! $is_current ) : ?>
592 </a>
593 <?php endif; ?>
594 (<?php printf( __('%d chars', 'so-css'), strlen( $css ) ) ?>)<?php if ( $i == 0 ) : ?> (<?php _e( 'Latest', 'so-css' ) ?>)<?php endif; ?>
595 </li>
596 <?php
597 $i++;
598 }
599 } else {
600 printf( '<em>%s</em>', __( 'No revisions yet.', 'so-css' ) );
601 }
602 }
603
604 /**
605 * Add one or more paths to the registered snippet paths
606 *
607 * @param string|array $path
608 */
609 function register_snippet_path( $path ) {
610 $this->snippet_paths = array_merge( $this->snippet_paths, (array) $path );
611 }
612
613 /**
614 * Get all the available snippets
615 *
616 * @return array|bool
617 */
618 function get_snippets() {
619 // Get the snippet paths
620 $snippet_paths = apply_filters( 'siteorigin_css_snippet_paths', $this->snippet_paths );
621 if ( empty( $snippet_paths ) ) {
622 return array();
623 }
624
625 static $snippets = array();
626 if ( ! empty( $snippets ) ) {
627 return $snippets;
628 }
629
630 if ( ! WP_Filesystem() ) {
631 return false;
632 }
633 global $wp_filesystem;
634 foreach ( $snippet_paths as $path ) {
635 foreach ( glob( $path . '/*.css' ) as $css_file ) {
636 $data = get_file_data( $css_file, array(
637 'Name' => 'Name',
638 'Description' => 'Description',
639 ) );
640
641 // Get the CSS and strip out the first header
642 $css = $wp_filesystem->get_contents( $css_file );
643 $css = preg_replace( '!/\*.*?\*/!s', '', $css, 1 );
644
645 $snippets[] = wp_parse_args( $data, array(
646 'css' => str_replace( "\t", ' ', trim( $css ) ),
647 ) );
648 }
649 }
650
651 usort( $snippets, array( $this, 'sort_snippet_callback' ) );
652
653 return $snippets;
654 }
655
656 /**
657 * Sort snippets by name.
658 *
659 * @param $a
660 * @param $b
661 *
662 * @return int
663 */
664 static function sort_snippet_callback( $a, $b ) {
665 return $a['Name'] > $b['Name'] ? 1 : - 1;
666 }
667
668 /**
669 * A very simple CSS sanitization function.
670 *
671 * @param $css
672 *
673 * @return string
674 */
675 static function sanitize_css( $css ) {
676 return trim( strip_tags( $css ) );
677 }
678
679 /**
680 * Get all the available theme CSS
681 */
682 function get_theme_css() {
683 $css = '';
684 if ( file_exists( get_template_directory() . '/style.css' ) ) {
685 $css .= file_get_contents( get_template_directory() . '/style.css' );
686 }
687
688 if ( is_child_theme() ) {
689 $css .= file_get_contents( get_stylesheet_directory() . '/style.css' );
690 }
691
692 // Remove all CSS comments
693 $regex = array(
694 "`^([\t\s]+)`ism" => '',
695 "`^\/\*(.+?)\*\/`ism" => "",
696 "`(\A|[\n;]+)/\*[^*]*\*+(?:[^/*][^*]*\*+)*/`" => "$1",
697 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n"
698 );
699 $css = preg_replace( array_keys( $regex ), $regex, $css );
700 $css = preg_replace( '/\s+/', ' ', $css );
701
702 return $css;
703 }
704
705 function enqueue_inspector_scripts() {
706 if ( ! current_user_can( 'edit_theme_options' ) ) {
707 return;
708 }
709
710 wp_enqueue_style( 'dashicons' );
711
712 wp_enqueue_script( 'siteorigin-css-parser-lib', plugin_dir_url( __FILE__ ) . 'js/css' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), SOCSS_VERSION );
713
714 wp_enqueue_script( 'siteorigin-css-sizes', plugin_dir_url( __FILE__ ) . 'js/jquery.sizes' . SOCSS_JS_SUFFIX . '.js', array( 'jquery' ), '0.33' );
715 wp_enqueue_script( 'siteorigin-css-specificity', plugin_dir_url( __FILE__ ) . 'js/specificity' . SOCSS_JS_SUFFIX . '.js', array() );
716 wp_enqueue_script( 'siteorigin-css-inspector', plugin_dir_url( __FILE__ ) . 'js/inspector' . SOCSS_JS_SUFFIX . '.js', array(
717 'jquery',
718 'underscore',
719 'backbone'
720 ), SOCSS_VERSION, true );
721 wp_enqueue_style( 'siteorigin-css-inspector', plugin_dir_url( __FILE__ ) . 'css/inspector.css', array(), SOCSS_VERSION );
722
723 wp_localize_script( 'siteorigin-css-inspector', 'socssOptions', array() );
724 }
725
726 function inspector_templates() {
727 if ( ! current_user_can( 'edit_theme_options' ) ) {
728 return;
729 }
730
731 include plugin_dir_path( __FILE__ ) . 'tpl/inspector-templates.php';
732 }
733
734 /**
735 * Change the stylesheets to all be inline
736 */
737 function inline_inspector_scripts() {
738 if ( ! current_user_can( 'edit_theme_options' ) ) {
739 return;
740 }
741
742 $regex = array(
743 "`^([\t\s]+)`ism" => '',
744 "`^\/\*(.+?)\*\/`ism" => "",
745 "`(\A|[\n;]+)/\*[^*]*\*+(?:[^/*][^*]*\*+)*/`" => "$1",
746 "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n"
747 );
748
749 global $wp_styles;
750 if ( empty( $wp_styles->queue ) ) {
751 return;
752 }
753
754 // Make each of the scripts inline
755 foreach ( $wp_styles->queue as $handle ) {
756 if ( $handle === 'siteorigin-css-inspector' || $handle === 'dashicons' ) {
757 continue;
758 }
759 $style = $wp_styles->registered[ $handle ];
760 if ( empty( $style->src ) || substr( $style->src, 0, 4 ) !== 'http' ) {
761 continue;
762 }
763 $response = wp_remote_get( $style->src );
764 if ( is_wp_error( $response ) || $response['response']['code'] !== 200 || empty( $response['body'] ) ) {
765 continue;
766 }
767
768 $css = $response['body'];
769 $css = preg_replace( array_keys( $regex ), $regex, $css );
770
771 ?>
772 <script type="text/css" class="socss-theme-styles"
773 id="socss-inlined-style-<?php echo sanitize_html_class( $handle ) ?>">
774 <?php echo strip_tags( $css ); ?>
775 </script>
776 <?php
777 }
778 }
779
780 function disable_ngg_resource_manager() {
781 if ( ! current_user_can( 'edit_theme_options' ) ) {
782 return;
783 }
784
785 //The NextGen Gallery plugin does some weird interfering with the output buffer.
786 define( 'NGG_DISABLE_RESOURCE_MANAGER', true );
787 }
788
789 private function get_latest_revision_timestamp() {
790 $revisions = $this->get_custom_css_revisions( $this->theme );
791 if ( empty( $revisions ) ) {
792 return false;
793 }
794 krsort( $revisions );
795 $revision_times = array_keys( $revisions );
796
797 return $revision_times[0];
798 }
799 }
800
801 // Initialize the single
802 SiteOrigin_CSS::single();
803