<?php
/*
Plugin Name: CDN Rewrite
Plugin URI: http://voceconnect.com/
Description: Rewrites asset URLs to CDN
Version: 0.1
Author: Chris Scott, Michael Pretty
Author URI: http://voceconnect.com/
*/

require_once('voce-settings.php');

class CDN_Rewrite {

	const OPTION_GENERAL = 'cdn_general';

	private $submenu_general;

	private $cdn_root_url;
	private $file_extensions;

	private $css_file_extensions;
	private $css_cdn_root_url;

	private $js_file_extensions;
	private $js_cdn_root_url;

	private $blog_details;

	public function __construct() {
		$this->cdn_root_url = $this->css_cdn_root_url = $this->js_cdn_root_url = untrailingslashit($this->get_setting('root_url'));

		if ($css_url = trim($this->get_setting('css_root_url'))) {
			$this->css_cdn_root_url = untrailingslashit($css_url);
		}

		if ($js_url = trim($this->get_setting('js_root_url'))) {
			$this->js_cdn_root_url = untrailingslashit($js_url);
		}

		$this->file_extensions = $this->get_setting('file_extensions');
		$this->css_file_extensions = $this->get_setting('css_file_extensions');
		$this->js_file_extensions = $this->get_setting('js_file_extensions');
	}

	public function initialize() {
		if (!class_exists('Voce_Settings')) {
			return;
		}

		add_action('admin_menu', array($this, 'add_options_page'));
		if ('' == $this->file_extensions || '' == $this->cdn_root_url) {
			add_action('admin_notices', array($this, 'settings_warning'));
			return;
		}

		if('/' != $this->cdn_root_url) {
			$action = (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) ? 'xmlrpc_call' : 'template_redirect';
			add_action($action, array($this, 'start_buffer'), 1);
		}
	}

	/**
	 * get general setting
	 *
	 * @param string $setting setting name
	 * @return mixed setting value or false if not set
	 */
	private function get_setting($setting) {
		$settings = get_option(self::OPTION_GENERAL);
		if(!$settings || !is_array($settings)) {
			$settings = array(
				'file_extensions' => 'bmp|bz2|gif|ico|gz|jpg|jpeg|mp3|pdf|png|rar|rtf|swf|tar|tgz|txt|wav|zip',
				'css_file_extensions' => 'css',
				'js_file_extensions' => 'js'
			);
			update_option(self::OPTION_GENERAL, $settings);
		}
		return (isset($settings[$setting])) ? $settings[$setting] : false;
	}

	public function settings_warning() {
		echo "<div class='update-nag'>The CDN Rewrite plugin is missing some required settings.</div>";
	}

	/**
	 * adds the options page
	 *
	 * @return void
	 */
	public function add_options_page() {
		$this->submenu_general = add_options_page('CDN Rewrite', 'CDN Rewrite', 'manage_options', self::OPTION_GENERAL, array($this, 'submenu_general'));
		$settings = new Voce_Settings(self::OPTION_GENERAL, self::OPTION_GENERAL);

		$section = $settings->add_section('api', 'CDN Rewrite Settings', $this->submenu_general);
		$section->add_field('root_url', 'CDN Root URL (required)', 'field_input', array('description' => 'The base URL of the CDN.'));
		$section->add_field('file_extensions', 'File Extensions (required)', 'field_input');
		$section->add_field('css_root_url', 'CDN Root URL for CSS Files (optional)', 'field_input', array('description' => 'The base URL of the CDN for CSS Files.'));
		$section->add_field('css_file_extensions', 'File Extensions for CSS Files (optional)', 'field_input');
		$section->add_field('js_root_url', 'CDN Root URL for JS Files (optional, defaults to Root URL)', 'field_input', array('description' => 'The base URL of the CDN for JS Files.'));
		$section->add_field('js_file_extensions', 'File Extensions for JS Files (optional, defaults to Root URL)', 'field_input');
	}

	/**
	 * callback to display submenu_external
	 *
	 * @return void
	 */
	function submenu_general() {
		?>
		<div class="wrap">
			<h2>CDN Rewrite Settings</h2>
			<form method="post" action="options.php">
				<?php settings_fields(self::OPTION_GENERAL); ?>
				<?php do_settings_sections($this->submenu_general); ?>
				<p class="submit">
					<input name="Submit" type="submit" class="button-primary" value="<?php esc_attr_e('Save Changes'); ?>" />
				</p>
			</form>
		</div>
		<?php
	}

	/**
	 * start output buffering.
	 *
	 */
	public function start_buffer() {
		ob_start(array($this, 'filter_urls'));
	}

	/**
	 * Callback for output buffering.  Search content for urls to replace
	 *
	 * @param string $content
	 * @return string
	 */
	public function filter_urls($content) {

		$root_url = $this->get_site_root_url();
		$xml_begin = $xml_end = '';
		if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
			$xml_begin = '>';
			$xml_end = '<';
		}
		$extensions = join('|', array_filter(array($this->file_extensions, $this->css_file_extensions, $this->js_file_extensions)));
		$regex = '#(?<=[(\"\''.$xml_begin.'])'.quotemeta($root_url).'(?:(/[^\"\''.$xml_end.')]+\.('.$extensions.')))#';
		$content = preg_replace_callback($regex, array($this, 'url_rewrite'), $content);

		return $content;
	}

	/**
	 * Returns the root url of the current site
	 *
	 * @return string
	 */
	public function get_site_root_url() {
		if(is_multisite() && !is_subdomain_install()) {
			$root_blog = get_blog_details(1);
			$root_url = $root_blog->siteurl;
		} else {
			$root_url = site_url();
		}
		return $root_url;
	}

	/**
	 * Returns the details for the current blog
	 *
	 * @return object
	 */
	public function get_this_blog_details() {
		if(!isset($this->blog_details)) {
			global $blog_id;
			$this->blog_details = get_blog_details($blog_id);
		}
		return $this->blog_details;
	}

	/**
	 * Callback for url preg_replace_callback.  Returns corrected URL
	 *
	 * @param array $match
	 * @return string
	 */
	public function url_rewrite($match) {
		global $blog_id;
		$path = $match[1];
		//if is subfolder install and isn't root blog and path starts with site_url and isnt uploads dir
		if(is_multisite() && !is_subdomain_install() && $blog_id !== 1) {
			$bloginfo = $this->get_this_blog_details();
			if((0 === strpos($path, $bloginfo->path)) && (0 !== strpos($path, $bloginfo->path.'files/'))) {
				$path = '/'.substr($path, strlen($bloginfo->path));
			}
		}
		if('/' !== $this->css_cdn_root_url && preg_match("/^.*\.(".$this->css_file_extensions.")$/i", $path) ) {
			return $this->css_cdn_root_url . $path;
		}
		if('/' !== $this->js_cdn_root_url && preg_match("/^.*\.(".$this->js_file_extensions.")$/i", $path) ) {
			return $this->js_cdn_root_url . $path;
		}
		return $this->cdn_root_url . $path;
	}

}
add_action('init', array(new CDN_Rewrite(), 'initialize'));

class CDN_VersionAssets {

	private $default_version = '';
	private $root_url;

	public function __construct() {
		$this->root_url = site_url();
	}

	public function initialize() {
		add_filter('style_loader_src', array($this, 'replace_version'), 10);
		add_filter('script_loader_src', array($this, 'replace_version'), 10);
		add_filter('stylesheet_uri', array($this, 'replace_version'), 10);
	}

	public function on_template_redirect() {
		$this->default_version = @filemtime(get_stylesheet_directory().'/style.css');
	}

	private function get_version($url) {
		if(0 === strpos($url, $this->root_url)) {
			$parts = parse_url($url);
			$file_path = str_replace(site_url('/'), ABSPATH, $parts['scheme'].'://'.$parts['host'].$parts['path']);
			if(	!($version = @filemtime($file_path)) ) {
				$version = $this->default_version;
			}
			return $version;
		}
		return false;
	}

	public function replace_version($src) {
		if( $new_version = $this->get_version($src) ) {
			return add_query_arg('ver', $new_version, $src);
		}
		return $src;
	}
}
add_action('init', array(new CDN_VersionAssets(), 'initialize'));