<?php
/**
 * Custom CSS and JS PRO
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}


/**
 * Get options for a specific custom-css-js post
 */
if ( ! function_exists( 'ccj_get_options' ) ) {
	function ccj_get_options( $post_id = 0, $language = 'css' ) {

		$default_options = array(
			'type'         => 'header',
			'linking'      => 'internal',
			'side'         => 'frontend',
			'language'     => 'css',
			'preprocessor' => 'none',
			'preid'        => '',
			'minify'       => false,
			'priority'     => 5,
		);

		if ( $language == 'html' ) {
			$default_options = array(
				'type'     => 'header',
				'linking'  => 'both',
				'side'     => 'frontend',
				'language' => 'html',
				'priority' => 5,
			);
		}

		if ( is_multisite() && is_super_admin() && is_main_site() ) {
			$default_options['multisite'] = false;
		}

		if ( empty( $post_id ) || $post_id == 0 ) {
			return $default_options;
		}

		$options = get_post_meta( $post_id, 'options', true );

		if ( ! is_array( $options ) || count( $options ) === 0 ) {
			return $default_options;
		}

		foreach ( $default_options as $_key => $_value ) {
			if ( ! isset( $options[ $_key ] ) ) {
				$options[ $_key ] = $_value;
			}
		}

		return $options;
	}
}


/**
 * Save the code into a file in CCJ_UPLOAD_DIR
 *
 * @var content string - the code to be saved
 * @var options array - the code options as returned by ccj_get_options
 * @var filename string - the filename of the code
 * @var post_id string - the post's id
 */
if ( ! function_exists( 'ccj_save_code_file' ) ) {
	function ccj_save_code_file( $content, $options, $filename = '', $post_id = '' ) {

		$content = stripslashes( $content );

		if ( $options['language'] == 'css' && $options['preprocessor'] != 'none' && empty( $options['preid'] ) ) {
			$content = ccj_modify_code( 'preprocess', $options['preprocessor'], $content, $post_id );
		}

		if ( $options['minify'] == '1' ) {
			$content = ccj_modify_code( 'minify', $options['language'], $content, $post_id );
		}

		if ( $options['language'] == 'js' ) {
			// Replace the default comment
			if ( preg_match( '@/\* Add your JavaScript code here[\s\S]*?End of comment \*/@im', $content ) ) {
				$content = preg_replace( '@/\* Add your JavaScript code here[\s\S]*?End of comment \*/@im', '/* Default comment here */', $content );
			}

			// For other locales remove all the comments
			if ( substr( get_locale(), 0, 3 ) !== 'en_' ) {
				$content = preg_replace( '@/\*[\s\S]*?\*/@', '', $content );
			}
		}

		$before = '';
		$after  = '';
		if ( $options['linking'] == 'internal' ) {
			$before = '<!-- start Simple Custom CSS and JS -->' . PHP_EOL;
			$after  = '<!-- end Simple Custom CSS and JS -->' . PHP_EOL;
			if ( $options['language'] == 'css' ) {
				$before .= '<style type="text/css">' . PHP_EOL;
				$after   = PHP_EOL . '</style>' . PHP_EOL . $after;
			}
			if ( $options['language'] == 'js' ) {
				if ( ! preg_match( '/<script\b[^>]*>([\s\S]*?)<\/script>/im', $content ) ) {
					$before .= '<script type="text/javascript">' . PHP_EOL;
					$after   = PHP_EOL . '</script>' . PHP_EOL . $after;
				} else {
					// the content has a <script> tag, then remove the comments so they don't show up on the frontend
					$content = preg_replace( '@/\*[\s\S]*?\*/@', '', $content );
				}
			}
		}

		if ( $options['language'] == 'css' && $options['preprocessor'] != 'none' && ! empty( $options['preid'] ) ) {
			$before = '';
			$after  = '';
		}

		if ( $options['linking'] == 'external' ) {
			$before = '/******* Do not edit this file *******' . PHP_EOL .
			'Simple Custom CSS and JS - by Silkypress.com' . PHP_EOL .
			'Saved: ' . date( 'M d Y | H:i:s' ) . ' */' . PHP_EOL;
		}

		if ( wp_is_writable( CCJ_UPLOAD_DIR ) ) {
			$file_content = $before . $content . $after;
			@file_put_contents( CCJ_UPLOAD_DIR . '/' . sanitize_file_name( $filename ), $file_content );

			// save the file as the Permalink slug
			$slug = get_post_meta( $options['post_id'], '_slug', true );
			if ( $slug ) {
				@file_put_contents( CCJ_UPLOAD_DIR . '/' . sanitize_file_name( $slug . '.' . $options['language'] ), $file_content );
			}
		}

		do_action( 'ccj_after_save_custom_code', $content, $options, $filename );
	}
}


/**
 * Compile and save again the SASS custom codes that use this partial
 *
 * @var content string - the code to be saved
 * @var options array - the code options as returned by ccj_get_options
 * @var filename string - the filename of the code
 */
add_action( 'ccj_after_save_custom_code', 'ccj_after_save_custom_code', 10, 3 );
if ( ! function_exists( 'ccj_after_save_custom_code' ) ) {
	function ccj_after_save_custom_code( $content, $options, $filename ) {
		if ( $options['language'] != 'css' || $options['preprocessor'] != 'sass' || empty( $options['preid'] ) ) {
			return false;
		}

		$posts = query_posts( 'post_type=custom-css-js&post_status=publish&nopaging=true&s=' . $options['preid'] );

		if ( ! $posts || count($posts) == 0 ) {
			return false;
		}

		foreach ( $posts as $_post ) {
			if ( get_post_meta( $_post->ID, '_active', true ) == 'no' ) continue;
			if ( ! preg_match( '/@import\W+["\']'.$options['preid'].'["\']/i', $_post->post_content ) ) continue;

			$_options = get_post_meta( $_post->ID, 'options', true );
			if ( isset( $_options['preid'] ) && ! empty( $_options['preid'] ) ) continue;

			$_filename = $_post->ID . '.css';

			ccj_save_code_file( $_post->post_content, $_options, $_filename, $_post->ID );

		}
	}
}


/**
 * Modify the code
 *
 * @var type string - allowed options: minify, preprocess
 * @var option string - allowed options: css, js, less, sass
 * @var content string - the code to modify
 * @var post_id string - the post's id 
 */
if ( ! function_exists( 'ccj_modify_code' ) ) {
	function ccj_modify_code( $type = 'minify', $option = 'ccs', $content = '', $post_id = '' ) {


		if ( $type == 'minify' ) {
			if ( ! class_exists( '\MatthiasMullie\Minify\Minify' ) ) {
					require_once 'vendor/path-converter/src/ConverterInterface.php';
					require_once 'vendor/path-converter/src/Converter.php';
					require_once 'vendor/minify/src/Minify.php';
					require_once 'vendor/minify/src/CSS.php';
					require_once 'vendor/minify/src/JS.php';
					require_once 'vendor/minify/src/Exception.php';
			}
			try {
				if ( $option == 'js' ) {
					$minifier = new \MatthiasMullie\Minify\JS( $content );
				}
				if ( $option == 'css' ) {
					$minifier = new \MatthiasMullie\Minify\CSS( $content );
				}
				$content = $minifier->minify();
			} catch ( Exception $e ) {
				set_site_transient( 'ccje_' . $post_id, $e->getMessage(), 3600 );
			}
		}

		if ( $type == 'preprocess' ) {

			/* Ideally the "ccj_preprocessor_import_path" filter should only add import paths to the paths array. */
			$import_paths = apply_filters( 'ccj_preprocessor_import_paths', array( CCJ_UPLOAD_DIR ) );

			if ( $option == 'less' ) {
				if ( ! method_exists( 'lessc', 'compile' ) ) {
					if ( version_compare( PHP_VERSION, '7.2' ) >= 0 ) {
						// https://github.com/wikimedia/less.php
						require_once 'vendor/wikimedia/less.php/lessc.inc.php';
					} else {
						// https://github.com/leafo/lessphp
						require_once 'vendor/lessc.inc.php';
					}
				}
				$compiler = new lessc();
				$compiler->setImportDir( $import_paths );
				try {
					return $compiler->compile( $content );
				} catch ( Exception $e ) {
					set_site_transient( 'ccje_' . $post_id, $e->getMessage(), 3600 );
				}
			}

			if ( $option == 'sass' ) {
				if ( defined( 'PHP_VERSION_ID' ) && PHP_VERSION_ID >= 70403 ) { // use the PHP_VERSION_ID from the vendor/composer/platform_check.php file

					require dirname( CCJ_PLUGIN_FILE_PRO ) . '/includes/vendor/autoload.php';

					// https://github.com/scssphp/scssphp
					$compiler = new \ScssPhp\ScssPhp\Compiler();
					$compiler->setImportPaths( $import_paths );
					try {
						// before version 1.4.1 the scssphp library was using the 'compile' method instead of 'compileString'
						// and, if another plugin loads an older version of the scssphp file, then a "Call to undefined method compileString()" error is thrown
						if ( method_exists( '\ScssPhp\ScssPhp\Compiler', 'compileString' ) ) {
							return $compiler->compileString($content)->getCss();
						} else {
							return $compiler->compile($content);
						}
					} catch ( Exception $e ) {
						set_site_transient( 'ccje_' . $post_id, $e->getMessage(), 3600 );
					}
				} else {
					// https://github.com/leafo/scssphp
					if ( ! class_exists( 'scssc' ) ) {
						require_once 'vendor/scss.inc.php';
					}
					$compiler = new scssc();
					$compiler->setImportPaths( $import_paths );
					try {
						return $compiler->compile( $content );
					} catch ( Exception $e ) {
						set_site_transient( 'ccje_' . $post_id, $e->getMessage(), 3600 );
					}
				}
			}
		}

		return $content;

	}
}



/**
 * Build a tree where you can quickly find the needed custom-css-js posts
 *
 * @return void
 */
if ( ! function_exists( 'ccj_build_search_tree' ) ) {
	function ccj_build_search_tree() {

		// Retrieve all the custom-css-js codes
		$posts = query_posts( 'post_type=custom-css-js&post_status=publish&nopaging=true' );

		$is_multisite_main_site = ( is_multisite() && is_super_admin() && is_main_site() ) ? true : false;

		$old_tree = get_option( 'custom-css-js-tree', [] );

		$tree           = array();
		$tree_multisite = array();
		$block_css      = array();
		$block_js       = array();
		foreach ( $posts as $_post ) {
			if ( ! ccj_is_active( $_post->ID ) ) {
				continue;
			}

			$options = ccj_get_options( $_post->ID );

			if ( ! empty( $options['preid'] ) ) {
				continue;
			}

			// Get the branch name, example: frontend-css-header-external
			$tree_branch = '[side]-' . $options['language'] . '-' . $options['type'] . '-' . $options['linking'];

			$filename = $_post->ID . '.' . $options['language'];

			if ( $options['linking'] == 'external' ) {
				$filename .= '?v=' . rand( 1, 10000 );
			}

			if ( $options['language'] == 'html' ) {
				$filename = $_post->ID;
			}

			if ( $options['language'] == 'html' && $options['type'] == 'shortcode' ) {
				$tree_branch         = 'shortcode';
				$options['priority'] = 10;
				$filename            = $_post->ID . '-' . $options['name'];
			}

			$priority = isset( $options['priority'] ) ? $options['priority'] : 5;

			// Add the code file to the tree branch
			$sides = explode( ',', $options['side'] );
			foreach ( $sides as $_side ) {
				$this_tree_branch = str_replace( '[side]', $_side, $tree_branch );
				$tree[ $priority ][ $this_tree_branch ][] = $filename;
			}

			if ( $is_multisite_main_site && isset( $options['multisite'] ) && $options['multisite'] == true ) {
				$sides = explode( ',', $options['side'] );
				foreach ( $sides as $_side ) {
					$this_tree_branch = str_replace( '[side]', $_side, $tree_branch ) . '-multisite';
					$tree_multisite[ $priority ][ $this_tree_branch ][] = $filename;
				}
			}


			// Mark to enqueue the jQuery library, if necessary
			if ( 'js' === $options['language'] && ( strstr($options['side'], 'frontend') || strstr( $options['side'], 'block' ) ) ) {
				$_post->post_content = preg_replace( '@/\* Add your JavaScript code here[\s\S]*?End of comment \*/@im', '/* Default comment here */', $_post->post_content );
				if ( preg_match( '/jquery\s*(\(|\.)/i', $_post->post_content ) && ! isset( $tree[5]['jquery'] ) ) {
					$tree[5]['jquery'] = true;
					if ( $is_multisite_main_site && isset( $options['multisite'] ) && $options['multisite'] == true ) {
						$tree_multisite[5]['jquery'] = true;
					}
				}
			}

			if ( strstr( $options['side'], 'block' ) && $options['linking'] === 'internal' ) {
				if ( $options['language'] === 'js' ) {
					$block_js[ $priority ] .= $_post->post_content . "\n";
				}
				if ( $options['language'] === 'css' ) {
					$block_css[ $priority ] .= $_post->post_content . "\n";
				}
				$tree[5]['block-internal-' . $options['language'] ] = true;
				if ( $is_multisite_main_site && isset( $options['multisite'] ) && $options['multisite'] == true ) {
					$tree_multisite[5]['block-internal-' . $options['language']] = true;
				}
			}
		}


		// Check both the old tree and the new tree for the "block" keys.
		// Then save the "block_js.js" and "block_css.css" files.
		if ( isset( $old_tree[5]['block-internal-css'] ) || isset( $tree[5]['block-internal-css'] ) ) {
			@file_put_contents( CCJ_UPLOAD_DIR . '/block_css.css', implode( "\n", $block_css ) );
		}
		if ( isset( $old_tree[5]['block-internal-js'] ) || isset( $tree[5]['block-internal-js'] ) ) {
			@file_put_contents( CCJ_UPLOAD_DIR . '/block_js.js', implode( "\n", $block_js ) );
		}

		// Save the tree in the database
		update_option( 'custom-css-js-tree', $tree );

		if ( $is_multisite_main_site ) {
			update_site_option( 'custom-css-js-multisite', $tree_multisite );
		}
	}
}



/**
 * Build a tree where you can quickly find the needed custom-css-js posts
 *
 * @return void
 */
/*
if ( ! function_exists( 'ccj_build_html_tree' ) ) {
function ccj_build_html_tree() {

	// Retrieve all the custom-css-js codes
	$posts = query_posts( 'post_type=custom-css-js&post_status=publish&nopaging=true' );

	$tree = array();
	foreach ( $posts as $_post ) {
		if ( ! ccj_is_active( $_post->ID ) ) {
			continue;
		}

		$options = ccj_get_options( $_post->ID, 'html' );

		if ( $options['language'] != 'html' ) {
			continue;
		}

		// Get the branch name, example: frontend-css-header-external
		$tree_branch = $options['side'] . '-' . $options['linking'];


		$priority = isset( $options['priority'] ) ? $options['priority'] : 5;

		// Add the code file to the tree branch
		$tree[$priority][ $tree_branch ][] = $_post->ID;

	}

	// Save the tree in the database
	update_option( 'custom-css-js-html-tree', $tree );
}
}
 */

/**
 * Check if a code is active
 *
 * @return bool
 */
if ( ! function_exists( 'ccj_is_active' ) ) {
	function ccj_is_active( $post_id ) {
		return get_post_meta( $post_id, '_active', true ) !== 'no';
	}
}


/**
 * Get an array with all the information for building the code's options
 */
if ( ! function_exists( 'ccj_get_options_meta' ) ) {
	function ccj_get_options_meta() {
		global $wp_version;

		$options = array(
			'linking'      => array(
				'title'   => __( 'Linking type', 'custom-css-js-pro' ),
				'type'    => 'radio',
				'default' => 'internal',
				'values'  => array(
					'external' => array(
						'title'    => __( 'External File', 'custom-css-js-pro' ),
						'dashicon' => 'media-code',
					),
					'internal' => array(
						'title'    => __( 'Internal', 'custom-css-js-pro' ),
						'dashicon' => 'editor-alignleft',
					),
				),
			),
			'type'         => array(
				'title'   => __( 'Where on page', 'custom-css-js-pro' ),
				'type'    => 'radio',
				'default' => 'header',
				'values'  => array(
					'header' => array(
						'title'    => __( 'In the <head> element', 'custom-css-js-pro' ),
						'dashicon' => 'arrow-up-alt2',
					),
					'footer' => array(
						'title'    => __( 'In the <footer> element', 'custom-css-js-pro' ),
						'dashicon' => 'arrow-down-alt2',
					),
				),
			),
			'side'         => array(
				'title'   => __( 'Where in site', 'custom-css-js-pro' ),
				'type'    => 'checkbox',
				'default' => 'frontend',
				'values'  => array(
					'frontend' => array(
						'title'    => __( 'In Frontend', 'custom-css-js-pro' ),
						'dashicon' => 'tagcloud',
					),
					'block'    => array(
						'title'    => __( 'In Block editor', 'custom-css-js-pro' ),
						'dashicon' => 'layout',
					),
					'admin'    => array(
						'title'    => __( 'In Admin', 'custom-css-js-pro' ),
						'dashicon' => 'id',
					),
					'login'    => array(
						'title'    => __( 'On Login Page', 'custom-css-js-pro' ),
						'dashicon' => 'admin-network',
					),
				),
			),
			'preprocessor' => array(
				'title'   => __( 'CSS Preprocessor', 'custom-css-js-pro' ),
				'type'    => 'radio',
				'default' => 'none',
				'values'  => array(
					'none' => array(
						'title' => __( 'None', 'custom-css-js-pro' ),
					),
					'sass' => array(
						'title' => __( 'SASS (only SCSS syntax)', 'custom-css-js-pro' ),
					),
					'less' => array(
						'title' => 'Less',
					),
				),
			),
			'minify'       => array(
				'title'    => __( 'Minify the code', 'custom-css-js-pro' ),
				'type'     => 'checkbox',
				'default'  => false,
				'dashicon' => 'editor-contract',
			),
			'priority'     => array(
				'title'    => __( 'Priority', 'custom-css-js-pro' ),
				'type'     => 'select',
				'default'  => 5,
				'dashicon' => 'sort',
				'values'   => array(
					1  => __( '1 (highest)', 'custom-css-js-pro' ),
					2  => '2',
					3  => '3',
					4  => '4',
					5  => '5',
					6  => '6',
					7  => '7',
					8  => '8',
					9  => '9',
					10 => __( '10 (lowest)', 'custom-css-js-pro' ),
				),
			),
		);

		if ( version_compare( $wp_version, '6.6', '<' ) ) {
			$in_block_tipsy = __( 'The "In Block editor" option is available only for "Linking type: External File". For WordPress >= 6.6 the "In Block editor" option is available for both linking types.', 'custom-css-js' );
			$in_block_tipsy = htmlentities( $in_block_tipsy );
			$in_block_tipsy = '<span rel="tipsy" class="dashicons dashicons-editor-help tipsy-no-html" style="margin: 7px 3px 0 3px; display: none;" title="' . $in_block_tipsy . '"></span>';
			$options['side']['values']['block']['title'] .= $in_block_tipsy;
		}

		if ( ! version_compare( $wp_version, '5.0' ) || class_exists( 'Classic_Editor' ) ) {
			// Remove the "In Block editor" option only for WP < 5 or if the "Classic Editor" plugin installed
			unset( $options['side']['values']['block'] );
		}

		if ( is_multisite() && is_super_admin() && is_main_site() ) {
			$options['multisite'] = array(
				'title'    => __( 'Apply network wide', 'custom-css-js-pro' ),
				'type'     => 'checkbox',
				'default'  => false,
				'dashicon' => 'admin-multisite',
			);
		}

		return $options;
	}
}


/**
 * Get an array with all the information for building the code's options
 */
if ( ! function_exists( 'ccj_get_options_meta_html' ) ) {
	function ccj_get_options_meta_html() {
		$options = array(
			'type'     => array(
				'title'   => __( 'Where on page', 'custom-css-js-pro' ),
				'type'    => 'radio',
				'default' => 'header',
				'values'  => array(
					'header'    => array(
						'title'    => __( 'In the &lt;head&gt; element', 'custom-css-js-pro' ),
						'dashicon' => 'arrow-up-alt2',
					),
					'footer'    => array(
						'title'    => __( 'In the &lt;footer&gt; element', 'custom-css-js-pro' ),
						'dashicon' => 'arrow-down-alt2',
					),
					'shortcode' => array(
						'title'    => __( 'As shortcode', 'custom-css-js-pro' ),
						'dashicon' => 'paperclip',
					),
				),
			),
			'side'     => array(
				'title'   => __( 'Where in site', 'custom-css-js-pro' ),
				'type'    => 'checkbox',
				'default' => 'frontend',
				'values'  => array(
					'frontend' => array(
						'title'    => __( 'In Frontend', 'custom-css-js-pro' ),
						'dashicon' => 'tagcloud',
					),
					'admin'    => array(
						'title'    => __( 'In Admin', 'custom-css-js-pro' ),
						'dashicon' => 'id',
					),
				),
			),
			'linking'  => array(
				'title'    => __( 'On which device', 'custom-css-js-pro' ),
				'type'     => 'radio',
				'default'  => 'both',
				'dashicon' => '',
				'values'   => array(
					'desktop' => array(
						'title'    => __( 'Desktop', 'custom-css-js-pro' ),
						'dashicon' => 'desktop',
					),
					'mobile'  => array(
						'title'    => __( 'Mobile', 'custom-css-js-pro' ),
						'dashicon' => 'smartphone',
					),
					'both'    => array(
						'title'    => __( 'Both', 'custom-css-js-pro' ),
						'dashicon' => 'tablet',
					),
				),
			),
			'priority' => array(
				'title'    => __( 'Priority', 'custom-css-js-pro' ),
				'type'     => 'select',
				'default'  => 5,
				'dashicon' => 'sort',
				'values'   => array(
					1  => __( '1 (highest)', 'custom-css-js-pro' ),
					2  => '2',
					3  => '3',
					4  => '4',
					5  => '5',
					6  => '6',
					7  => '7',
					8  => '8',
					9  => '9',
					10 => __( '10 (lowest)', 'custom-css-js-pro' ),
				),
			),
		);

		if ( function_exists( 'wp_body_open' ) ) {
			$tmp = $options['type']['values'];
			unset( $options['type']['values'] );
			$options['type']['values']['header']    = $tmp['header'];
			$options['type']['values']['body_open'] = array(
				'title'    => __( 'After &lt;body&gt; tag', 'custom-css-js-pro' ),
				'dashicon' => 'editor-code',
			);
			$options['type']['values']['footer']    = $tmp['footer'];
			$options['type']['values']['shortcode'] = $tmp['shortcode'];
		}

		if ( is_multisite() && is_super_admin() && is_main_site() ) {
			$options['multisite'] = array(
				'title'    => __( 'Apply network wide', 'custom-css-js-pro' ),
				'type'     => 'checkbox',
				'default'  => false,
				'dashicon' => 'admin-multisite',
			);
		}

		return $options;
	}
}


/**
 * Make a link to the documentation
 */
if ( ! function_exists( 'ccj_a_doc' ) ) {
	function ccj_a_doc( $link ) {

		$output = '<a href="' . $link . '" target="_blank" class="dashicons"><span class="dashicons dashicons-editor-help" rel="tipsy" original-title="' . __( 'Click for the documentation', 'custom-css-js-pro' ) . '"></span></a>';

		return $output;
	}
}

