<?php
/**
 * @version    $Id$
 * @package    IT_Theme_Framework
 * @author     InnoThemes Team <support@innothemes.com>
 * @copyright  Copyright (C) 2012 InnoThemes.com. All Rights Reserved.
 * @license    GNU/GPL v2 or later http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Websites: http://www.innothemes.com
 */

class IT_Gadget_Sample_Data extends IT_Gadget_Base {
	/**
	 * Gadget file name without extension.
	 *
	 * @var  string
	 */
	protected $gadget = 'sample-data';

	/**
	 * Root URL ( without trailing slash ) for downloading sample data package.
	 *
	 * @var  string
	 */
	protected $_download_root = 'http://www.innothemes.com/wp-themes/';

	/**
	 * Constructor.
	 *
	 * @return  void
	 */
	public function __construct() {
		// Init WordPress Filesystem Abstraction
		function_exists( 'WP_Filesystem' ) || include_once ABSPATH . 'wp-admin/includes/file.php';

		global $wp_filesystem;

		isset( $wp_filesystem ) || WP_Filesystem();

		// Init WordPress Plugin Upgrader
		class_exists( 'Plugin_Upgrader' ) || include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';

		function_exists( 'screen_icon'     ) || include_once ABSPATH . 'wp-admin/includes/screen.php';
		function_exists( 'show_message'    ) || include_once ABSPATH . 'wp-admin/includes/misc.php';
		function_exists( 'get_plugin_data' ) || include_once ABSPATH . 'wp-admin/includes/plugin.php';

		// Init WordPress Importer plugin
		function_exists( 'wp_insert_category' ) || include_once ABSPATH . 'wp-admin/includes/taxonomy.php';
		function_exists( 'post_exists'        ) || include_once ABSPATH . 'wp-admin/includes/post.php';
		function_exists( 'comment_exists'     ) || include_once ABSPATH . 'wp-admin/includes/comment.php';
	}

	/**
	 * User confirmed action.
	 *
	 * @return  void
	 */
	protected function confirmed_action() {
		// Generate link to download sample data package
		$link = $this->_download_root . str_replace( '-', '_', IT_THEME_ID ) . '_sample_data.zip';

		$this->set_response( 'success', $link );
	}

	/**
	 * Download action.
	 *
	 * @return  mixed  Either an array of plugins or null.
	 */
	protected function download_action() {
		try {
			// Generate path to store downloaded sample data package
			$path = wp_upload_dir();
			$path = $path['basedir'] . '/' . IT_THEME_ID . '-sample-data.zip';

			// Download sample data package if necessary
			if ( ! isset( $_GET['uploaded'] ) || ! ( int ) $_GET['uploaded'] ) {
				// Generate link to download sample data package
				$link = $this->_download_root . str_replace( '-', '_', IT_THEME_ID ) . '_sample_data.zip';

				// Download sample data package
				$this->download( $link, $path );
			}

			// Get sample data XML declaration
			$xml = $this->_parse_sample_data();

			if ( is_string( $xml ) ) {
				// Set response
				$this->set_response( 'outdate', sprintf( __( 'The theme version %s you are using is outdated. You need to update theme to the latest version before installing sample data.', IT_THEME_TEXTDOMAIN ), $xml ) );

				// Clean-up
				$this->_clean_temporary_data();

				return;
			}

			// Parse sample data declaration for dependencies
			foreach ( $xml->xpath( '//product/extension[@downloadurl]' ) as $plugin ) {
				$pluginAttrs = ( array ) $plugin->attributes();
				$pluginAttrs = $pluginAttrs['@attributes'];

				$pluginAttrs['version']      = isset( $pluginAttrs['version'] ) ? $pluginAttrs['version'] : '';
				$pluginAttrs['state']        = $this->_get_plugin_state( ( string ) $pluginAttrs['name'], ( string ) $pluginAttrs['version'] );
				$pluginAttrs['dependencies'] = array();

				if ( isset( $plugin->dependency ) && isset( $plugin->dependency->parameter ) ) {
					foreach ( $plugin->dependency->parameter as $name ) {
						if ( empty( $name ) ) {
							continue;
						}

						// Get dependency data
						$dependency = $xml->xpath( '//product/extension[@name="' . $name . '"]' );

						if ( ! $dependency || ! @count( $dependency ) ) {
							continue;
						}

						$dependency = current( $dependency );

						$dependencyAttrs = ( array ) $dependency->attributes();
						$dependencyAttrs = $dependencyAttrs['@attributes'];

						$dependencyAttrs['state'] = $this->_get_plugin_state( ( string ) $dependencyAttrs['name'], ( string ) $dependencyAttrs['version'] );

						if ( 'installed' != $dependencyAttrs['state'] ) {
							$pluginAttrs['dependencies'][] = $dependencyAttrs;
						}
					}
				}

				if ( 'installed' != $pluginAttrs['state'] || count( $pluginAttrs['dependencies'] ) ) {
					if ( isset( $pluginAttrs['required'] ) && 'true' == ( string ) $pluginAttrs['required'] ) {
						$plugins['required'][] = $pluginAttrs;
					} else {
						$plugins['optional'][] = $pluginAttrs;
					}
				}
			}

			return isset( $plugins ) ? $plugins : null;
		}
		catch ( Exception $e ) {
			throw $e;
		}
	}

	/**
	 * Upload action.
	 *
	 * @return  void
	 */
	protected function upload_action() {
		try {
			if ( ! isset( $_FILES['package'] ) || 0 != $_FILES['package']['error'] ) {
				throw new Exception( __( 'File upload failed.', IT_THEME_TEXTDOMAIN ) );
			}

			// Generate path to store downloaded sample data package
			$path = wp_upload_dir();
			$path = $path['basedir'] . '/' . IT_THEME_ID . '-sample-data.zip';

			// Move uploaded file to temporary location
			if ( ! move_uploaded_file( $_FILES['package']['tmp_name'], $path ) ) {
				throw new Exception( __( 'File upload failed.', IT_THEME_TEXTDOMAIN ) );
			}
		}
		catch ( Exception $e ) {
			throw $e;
		}
	}

	/**
	 * Install plugin action.
	 *
	 * @return  void
	 */
	protected function install_plugin_action() {
		try {
			// Get plugin being installed
			$plugin = isset( $_GET['plugin'] ) ? $_GET['plugin'] : null;

			if ( empty( $plugin ) ) {
				throw new Exception( __( 'Missing parameter.', IT_THEME_TEXTDOMAIN ) );
			}

			// Get sample data XML declaration
			$xml = $this->_parse_sample_data();

			// Get plugin details
			$plugin = $xml->xpath( '//product/extension[@name="' . $plugin . '"]' );
			$plugin = current( $plugin );
			$state  = $this->_get_plugin_state( ( string ) $plugin['name'], ( string ) $plugin['version'] );

			if ( 'installed' != $state ) {
				// Generate path to store downloaded plugin package
				$path = wp_upload_dir();
				$path = $path['basedir'] . '/' . IT_THEME_ID . '-sample-data/' . dirname( ( string ) $plugin['name'] ) . '.zip';

				// Download requested plugin package
				$valid_headers = array(
					'content-type' => array(
						'application/zip',
						'application/x-zip',
						'application/x-zip-compressed',
						'application/octet-stream',
						'application/x-compress',
						'application/x-compressed',
						'multipart/x-zip',
					)
				);

				$this->download( ( string ) $plugin['downloadurl'], $path, $valid_headers );

				if ( 'update' == $state ) {
					global $wp_filesystem;

					// Remove current plugin installation first
					uninstall_plugin( ( string ) $plugin['name'] );

					// Get the base plugin folder
					$plugins_dir = $wp_filesystem->wp_plugins_dir();

					if ( empty( $plugins_dir ) ) {
						throw new Exception( sprintf( __( 'Plugin installation failed: %s', IT_THEME_TEXTDOMAIN ), __( 'Unable to locate WordPress Plugin directory.', IT_THEME_TEXTDOMAIN ) ) );
					}

					$plugins_dir     = trailingslashit( $plugins_dir );
					$this_plugin_dir = trailingslashit( dirname( $plugins_dir . ( string ) $plugin['name'] ) );

					// If plugin is in its own directory, recursively delete the directory
					if ( strpos( ( string ) $plugin['name'], '/' ) && $this_plugin_dir != $plugins_dir ) {
						// Base check on if plugin includes directory separator && that its not the root plugin folder
						$deleted = $wp_filesystem->delete( $this_plugin_dir, true );
					} else {
						$deleted = $wp_filesystem->delete( $plugins_dir . ( string ) $plugin['name'] );
					}

					if ( ! $deleted ) {
						throw new Exception(
							sprintf(
								__( 'Plugin installation failed: %s', IT_THEME_TEXTDOMAIN ),
								__( 'Cannot remove existing plugin directory/file prior to update', IT_THEME_TEXTDOMAIN )
							)
						);
					}
				}

				// Init WordPress Plugin Upgrader
				$upgrader = new Plugin_Upgrader();

				// Let WordPress install requested plugin
				$result = $upgrader->install( ( string ) $plugin['downloadurl'] );

				if ( ! $result || is_wp_error( $result ) ) {
					throw new Exception(
						sprintf(
							__( 'Plugin installation failed: %s', IT_THEME_TEXTDOMAIN ),
							$result ? $result->get_error_message() : __( 'Unknown reason', IT_THEME_TEXTDOMAIN )
						)
					);
				}

				// Let WordPress activate the newly installed plugin
				$result = activate_plugin( ( string ) $plugin['name'], '', is_network_admin() );

				if ( is_wp_error( $result ) ) {
					throw new Exception( sprintf( __( 'Plugin activation failed: %s', IT_THEME_TEXTDOMAIN ), $result->get_error_message() ) );
				}
			}
		}
		catch ( Exception $e ) {
			throw $e;
		}
	}

	/**
	 * Import sample data action.
	 *
	 * @return  mixed  Either an array of skipped plugins or null.
	 */
	protected function import_data_action() {
		global $wpdb, $table_prefix, $wp_filesystem;

		// Increase maximum execution time
		if ( ! ini_get( 'safe_mode' ) ) {
			if ( function_exists( 'set_time_limit' ) ) {
				set_time_limit( 0 );
			} elseif ( function_exists( 'ini_set' ) ) {
				ini_set( 'max_execution_time', 0 );
			}
		}

		try {
			// Get path to sample data XML declaration file
			$xml = $this->_extract_package();

			// Parse sample data package
			$xml = $this->_parse_sample_data();

			// Pre-define database backup queries
			$backups = array();

			foreach ( $xml->xpath( '//product/extension' ) as $plugin ) {
				// Check plugin installation state
				if ( 'wordpress' != $plugin['name'] && isset( $plugin['downloadurl'] ) ) {
					$pluginAttrs = ( array ) $plugin->attributes();
					$pluginAttrs = $pluginAttrs['@attributes'];

					$pluginAttrs['version'] = isset( $pluginAttrs['version'] ) ? $pluginAttrs['version'] : '';
					$pluginAttrs['state']   = $this->_get_plugin_state( ( string ) $pluginAttrs['name'], ( string ) $pluginAttrs['version'] );

					if ( 'installed' != $pluginAttrs['state'] ) {
						$skipped_plugins[] = $pluginAttrs;

						continue;
					}
				}

				// Backup current plugin data
				$tables = $plugin->xpath( 'task[@name="dbbackup"]/parameters/parameter' );

				if ( $tables && count( $tables ) ) {
					foreach ( $tables as $table ) {
						// Set real table prefix
						$table = ( 'wp_' == substr( $table, 0, 3 ) ) ? str_replace( 'wp_', $table_prefix, $table ) : $table_prefix . $table;

						// Drop existing table first
						$backups[] = "DROP TABLE IF EXISTS `{$table}`;";

						// Get table creation schema
						$results   = $wpdb->get_results( "SHOW CREATE TABLE `{$table}`;", ARRAY_A );
						$results   = str_replace( 'CREATE TABLE', 'CREATE TABLE IF NOT EXISTS', $results[0]['Create Table'] );
						$backups[] = str_replace( "\n", '', $results ) . ';';

						// Get table data
						$results = $wpdb->get_results( "SELECT * FROM `{$table}` WHERE 1;", ARRAY_A );

						foreach ( $results as $result ) {
							$keys      = '(`' . implode( '`, `', array_keys( $result ) ) . '`)';
							$values    = "('" . implode( "', '", array_values( $result ) ) . "')";
							$backups[] = "INSERT INTO `{$table}` {$key} VALUES {$values};";
						}
					}
				}

				// Import sample plugin data
				$queries = $plugin->xpath( 'task[@name="dbinstall"]/parameters/parameter' );

				if ( $queries && count( $queries ) ) {
					foreach ( $queries as $query ) {
						// Set real table prefix
						$query = preg_replace(
							'/(DROP TABLE IF EXISTS|CREATE TABLE IF NOT EXISTS|DELETE FROM|INSERT INTO)\s+`?(wp_)?([^`\s]+)`?\s+/i',
							"\\1 {$table_prefix}\\3 ",
							$query
						);

						// Execute query
						$wpdb->query( $query );
					}
				}
			}

			// Create database backup file if necessary
			if ( count( $backups ) ) {
				$backups = implode( "\n", $backups );

				// Get path to store database backup file
				$path = wp_upload_dir();
				$path = $path['basedir'] . '/' . IT_THEME_ID . '/backup';

				if ( ! @is_dir( $path ) ) {
					$results = explode( '/', str_replace( '\\', '/', $path ) );
					$path    = array();

					while ( count( $results ) ) {
						$path[] = current( $results );

						if ( ! @is_dir( implode( '/', $path ) ) ) {
							$wp_filesystem->mkdir( implode( '/', $path ), 0755 );
						}

						// Shift paths
						array_shift( $results );
					}
				}

				$path = ( is_array( $path ) ? implode( '/', $path ) : $path ) . '/' . date( 'Y-m-d H-i-s' ) . '.sql';

				// Write database backup to file
				if ( ! $wp_filesystem->put_contents( $path, $backups ) ) {
					// If file creation failed, chmod file to 0644 and try again
					$wp_filesystem->chmod( $target, 0644 );

					$wp_filesystem->put_contents( $path, $backups );
				}
			}

			// Clean-up temporary file/directory left by sample data installation process
			$this->_clean_temporary_data();

			return isset( $skipped_plugins ) ? $skipped_plugins : null;
		}
		catch ( Exception $e ) {
			throw $e;
		}
	}

	/**
	 * Restore backup data action.
	 *
	 * @return  void
	 */
	protected function restore_backup_action() {
		global $wpdb, $wp_filesystem;

		// Increase maximum execution time
		if ( ! ini_get( 'safe_mode' ) ) {
			if ( function_exists( 'set_time_limit' ) ) {
				set_time_limit( 0 );
			} elseif ( function_exists( 'ini_set' ) ) {
				ini_set( 'max_execution_time', 0 );
			}
		}

		// Check if we have any backup file?
		$path = wp_upload_dir();
		$path = $path['basedir'] . '/' . IT_THEME_ID . '/backup';

		if ( count( $files = glob( "{$path}/*.sql" ) ) ) {
			// Sort by file name
			sort( $files );

			// Store latest backup file
			$backup = array_pop( $files );
		}

		if ( isset( $backup ) ) {
			// Read backup file
			if ( $backup = $wp_filesystem->get_contents( $backup ) ) {
				$backup = explode( "\n", $backup );

				// Loop thru SQL queries and execute
				foreach ( $backup as $query ) {
					$wpdb->query( $query );
				}
			}

			$this->set_response( 'success', __( 'Restoration successfully completed.', IT_THEME_TEXTDOMAIN ) );
		} else {
			throw new Exception( __( 'Not found any data backup file.', IT_THEME_TEXTDOMAIN ) );
		}
	}

	/**
	 * Remove data backup action.
	 *
	 * @return  void
	 */
	protected function remove_backup_action() {
		global $wp_filesystem;

		// Check if we have any backup file?
		$path = wp_upload_dir();
		$path = $path['basedir'] . '/' . IT_THEME_ID . '/backup';

		if ( count( $files = glob( "{$path}/*.sql" ) ) ) {
			foreach ( $files as $file ) {
				$wp_filesystem->delete( $file );
			}
		}

		$this->set_response( 'success', __( 'Removed all backup files.', IT_THEME_TEXTDOMAIN ) );
	}

	/**
	 * Extract sample data package.
	 *
	 * @return  string  Path to sample data XML declaration file.
	 */
	private function _extract_package() {
		// Generate path to store downloaded sample data package
		$path = wp_upload_dir();
		$path = $path['basedir'] . '/' . IT_THEME_ID . '-sample-data';
		$file = "{$path}.zip";

		// Check if sample data package is already extracted
		if ( @is_dir( $path ) ) {
			// Search for XML file
			$xml = glob( "{$path}/*.xml" );

			if ( count( $xml ) ) {
				return current( $xml );
			}
		}

		// Extract downloaded sample data package
		$result = unzip_file( $file, $path );

		if ( is_wp_error( $result ) ) {
			throw new Exception( sprintf( __( 'Unable to extract sample data package: %s', IT_THEME_TEXTDOMAIN ), $result->get_error_message() ) );
		}

		// Search for XML file
		$xml = glob( "{$path}/*.xml" );

		if ( count( $xml ) ) {
			return current( $xml );
		} else {
			throw new Exception( sprintf( __( 'Invalid sample data package: %s', IT_THEME_TEXTDOMAIN ), __( 'XML file not found', IT_THEME_TEXTDOMAIN ) ) );
		}
	}

	/**
	 * Parse sample data XML declaration.
	 *
	 * @return  object  An object instance of SimpleXmlElement class.
	 */
	private function _parse_sample_data() {
		try {
			// Get path to sample data XML declaration file
			$xml = $this->_extract_package();

			// Parse sample data XML declaration file
			$xml = simplexml_load_file( $xml );

			if ( ! $xml ) {
				throw new Exception( __( 'Unable to parse XML file for sample data declaration.', IT_THEME_TEXTDOMAIN ) );
			}
		}
		catch ( Exception $e ) {
			throw $e;
		}

		// Get theme data
		$theme = wp_get_theme();

		// Check if sample data is compatible with installed theme
		if ( ! isset( $xml['id'] ) || IT_THEME_ID != ( string ) $xml['id'] ) {
			if ( ! isset( $xml['name'] ) || IT_THEME_NAME != ( string ) $xml['name'] ) {
				throw new Exception(
					sprintf(
						__( 'Invalid sample data package: %s', IT_THEME_TEXTDOMAIN ),
						__( 'Theme name in sample data package does not match the one installed', IT_THEME_TEXTDOMAIN )
					)
				);
			}
		}

		// Check if sample data is compatible with installed theme version
		if ( '' != ( string ) $xml->product['version'] && version_compare( $theme->get( 'Version' ), ( string ) $xml->product['version'], '<' ) ) {
			return $theme->get( 'Version' );
		}

		return $xml;
	}

	/**
	 * Get plugin state.
	 *
	 * @param   string  $plugin   Plugin name.
	 * @param   string  $version  Plugin version.
	 *
	 * @return  string  Either install, update or installed.
	 */
	private function _get_plugin_state( $plugin, $version = '' ) {
		// Get installed plugin list
		$wp_plugins = get_plugins();

		// Look for requested plugin
		if ( array_key_exists( $plugin, $wp_plugins ) ) {
			if ( ! empty( $version ) && version_compare( $version, $wp_plugins[ $plugin ]['Version'], '>' ) ) {
				return 'update';
			} else {
				return 'installed';
			}
		}

		return 'install';
	}

	/**
	 * Clean-up temporary file/directory left by sample data installation process.
	 */
	private function _clean_temporary_data() {
		global $wp_filesystem;

		// Generate path to store downloaded sample data package
		$path = wp_upload_dir();
		$path = $path['basedir'] . '/' . IT_THEME_ID . '-sample-data';
		$file = "{$path}.zip";

		// It's time to clean-up
		$wp_filesystem->delete( $path, true );
		$wp_filesystem->delete( $file );
	}
}
