<?php
namespace TrxUpdater\Core\Update;

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

class Plugins extends Base {

	/**
	 * List of plugins to be updated
	 *
	 * List of plugins (registered in TGMPA by current theme) to be updated
	 *
	 * @since 1.0.0
	 * @access private
	 *
	 * @var plugins_list
	 */
	private $plugins_list;

	/**
	 * Plugin's parts to save while upgrade
	 *
	 * Save plugin's parts before upgrade
	 *
	 * @since 1.4.2
	 * @access private
	 *
	 * @var theme_parts
	 */
	private $plugins_parts;


	/**
	 * Class constructor.
	 *
	 * Initializing plugins update manager.
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function __construct( $manager ) {

		parent::__construct( $manager );

		$this->plugins_list = array();

		if ( (int) trx_updater_get_value_gp( 'trx_updater' ) > 0 ) {
			add_action( 'init', array( $this, 'remove_3rd_plugins_hooks'), 2000 );
		}

		add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ), 0 );

		add_action( 'tgmpa_register', array( $this, 'tgmpa_register' ), 2000 );

		add_filter( 'wp_get_update_data', array( $this, 'add_plugins_to_update_counts' ), 10, 2 );
		add_action( 'core_upgrade_preamble', array( $this, 'add_plugins_to_update_screen' ), 9 );

		// Before update plugin
		add_filter( 'upgrader_package_options', array( $this, 'before_plugin_upgrade' ), 10, 1 );

		// After update plugin
		add_filter( 'update_plugin_complete_actions', array( $this, 'after_plugin_upgrade' ), 10, 2 );
		add_filter( 'update_bulk_plugins_complete_actions', array( $this, 'after_plugin_upgrade' ), 10, 2 );

		// Fix for LatePoint
		add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'fix_latepoint_save_plugin_package' ), 1 );
		add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'fix_latepoint_restore_plugin_package' ), 9999 );

		// Fix for Slider Revolution
		if ( get_option('revslider-valid', 'false') == 'false' ) {
			add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'fix_revslider_save_plugin_package' ), 1 );
			add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'fix_revslider_restore_plugin_package' ), 9999 );
		}
		
		// Fix for Booked (old versions 2.4.3-)
		add_action( 'init', array( $this, 'fix_booked_disable_clear_update_info' ), 9999 );
	}

	/**
	 * Remove upgrade-specific hooks from 3rd-party plugins
	 *
	 * Remove upgrade-specific hooks from 3rd-party plugins to avoid break the update process
	 *
	 * Fired by `init` action with priority 2000 (after all other handlers).
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function remove_3rd_plugins_hooks() {
		global $wp_filter;
		// Remove VC hook
		// VC add filter: add_filter( 'upgrader_pre_download', array( $this, 'preUpgradeFilter' ), 10, 4 );
		$tag = 'upgrader_pre_download';
		$r = false;
		if ( ! empty( $wp_filter[ $tag ]->callbacks[10] ) && is_array( $wp_filter[ $tag ]->callbacks[10] ) ) {
			foreach( $wp_filter[ $tag ]->callbacks[10] as $k=>$v ) {
				if ( strpos( $k, 'preUpgradeFilter' ) > 0 ) {
					unset( $wp_filter[ $tag ]->callbacks[10][$k] );
				}
			}
		}
	}


	/**
	 * Add TGMPA menu item 'Install plugins' to the network admin menu
	 *
	 * Add link to the network admin menu 'Appearance - Install plugins' to enable update plugins via TGMPA
	 *
	 * Fired by `network_admin_menu` action with priority 0 (before all other handlers).
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function network_admin_menu() {
		if ( empty( $GLOBALS['tgmpa'] ) ) return;
		$instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
		if ( method_exists( $instance, 'admin_menu' ) ) {
			$instance->admin_menu();
		}
	}



	/**
	 * Edit URL of each plugin to get it from upgrade server.
	 *
	 * Substitute URLs in the plugins list to get its from upgrade server.
	 *
	 * Fired by `tgmpa_register` action with priority 2000 (after all other handlers).
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function tgmpa_register() {
		if ( empty( $GLOBALS['tgmpa'] ) ) return;
		$instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
		if ( ! empty( $instance->plugins ) ) {
			// Modify the parameter 'source' for each registered plugin -> get plugin from our update server
			$plugins_list = $this->get_plugins_list( $instance->plugins );
			foreach( $plugins_list as $k => $v ) {
				$slug = $k;
				if ( isset( $instance->plugins[ $slug ] ) && ! empty( $instance->plugins[ $slug ]['source'] ) ) {
					$this->plugins_list[$k] = $v;
					// Replace source path from theme dir to upgrade server for each plugin packed with theme
					// (exclude 'trx_addons')
					if ( $slug != 'trx_addons' && strpos( $instance->plugins[ $slug ]['source'], trx_updater_remove_protocol( $this->update_url ) ) === false ) {
						if ( ! empty( $this->theme_key ) ) {
							$instance->plugins[ $slug ]['source'] = sprintf( $this->update_url . '?action=install_plugin&src=%1$s&key=%2$s&theme_slug=%3$s&theme_name=%4$s&plugin=%5$s&domain=%6$s&rnd=%7$s',
								urlencode( $this->get_theme_market_code() ),
								urlencode( $this->theme_key ),
								urlencode( $this->theme_slug ),
								urlencode( $this->theme_name ),
								urlencode( "{$slug}/{$slug}.zip" ),
								urlencode( trx_updater_remove_protocol( get_home_url(), true ) ),
								mt_rand()
							);
						}
					}
				}
			}
			// Delete cache after the update each plugin
			if ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) {
				delete_transient( 'trx_updater_plugins_list' );
			}
		}
	}

	/**
	 * Detect plugin state
	 *
	 * Return state of the specified plugin
	 *
	 * @param string $slug	Plugin slug
	 * @return string
	 * 
	 * @since 1.0.0
	 * @access private
	 */
	private function check_plugin_state( $slug ) {
		$state = 'not installed';
		if ( is_dir( ABSPATH . 'wp-content/plugins/' . $slug . '/' ) ) {
			$state = 'installed';
			$plugins = get_option( 'active_plugins', array() );
			if ( is_multisite() ) {
				$mu_plugins = get_site_option( 'active_sitewide_plugins');
				if (is_array($mu_plugins)) {
					$plugins = array_merge($plugins, array_keys($mu_plugins));
				}
			}
			if (is_array($plugins)) {
				foreach($plugins as $p) {
					if (strpos($p, $slug . '/') !== false) {
						$state = 'active';
						break;
					}
				}
			}
		}
		return $state;
	}


	/**
	 * List of installed plugins and their versions (current and new).
	 *
	 * Return list of installed plugins and their versions (current and new). Clear cache every 12 hours.
	 *
	 * @since 1.0.0
	 * @access private
	 */
	private function get_plugins_list( $required_plugins ) {
		$list = get_transient( 'trx_updater_plugins_list' );
		if ( ! is_array( $list ) || ! empty( $_GET['force-check'] ) ) {
			$list = $this->get_plugins_info( $required_plugins );
			if ( is_array( $list ) ) {
				set_transient( 'trx_updater_plugins_list', $list, 4 * 60 * 60 );       // Store to the cache for 4 hours
			}
		}
		return $list;
	}

	/**
	 * Retrieve info about specified plugins
	 *
	 * Retrieve info about specified plugins from the updates server
	 *
	 * @since 1.0.0
	 * @access private
	 */
	private function get_plugins_info( $required_plugins ) {
		$info = array();
		$plugins = get_plugins();
		if ( is_array( $required_plugins ) ) {
			$data = get_transient( 'trx_updater_plugins_info' );
			if ( ! is_array( $data ) || ! empty( $_GET['force-check'] ) ) {
				$list = '';
				$data = array();
				foreach ( $required_plugins as $k => $v ) {
					$list .= ( ! empty( $list ) ? ',' : '' ) . $k;
					$data[ $k ] = array(
										'version' => '0.0.1',
										'update_from' => '0.0.2',
										);
				}
				$response = trx_updater_fgc( $this->update_url . '?action=info_plugins&plugins=' . urlencode( $list ) );
				if ( !empty($response) && is_serialized($response) ) {
					$response = unserialize($response);
					if ( !empty($response['data']) && substr($response['data'], 0, 1) == '{' ) {
						$data = json_decode($response['data'], true);
					}
				}
				set_transient( 'trx_updater_plugins_info', $data, 2 * 24 * 60 * 60 );       // Store to the cache for 2 days
			}
			if ( is_array($data) ) {
				foreach ( $required_plugins as $k => $v ) {
					$slug    = $k;
					$path    = "{$k}/{$k}.php";
					$title   = '';
					$version = '0.0.1';
					foreach ( $plugins as $p => $pv ) {
						$parts = explode( '/', $p );
						if ( $parts[0] == $k ) {
							$path    = $p;
							$title   = $pv['Title'];
							$version = $pv['Version'];
						}
					}
					// Check if this plugin can be updated:
					// if his version greater or equal to version in the field 'update_from' in the plugin's data received from the upgrade server
					// or if the field 'update_from' is absent ( any version can be updated )
					// If a current plugin is 'trx_addons' and an archive 'trx_addons.zip' is present in the theme folder, then the plugin can't be updated
					if ( ! empty( $title )
						&& ! empty( $data[$slug]['version'] )
						&& ( empty( $data[$slug]['update_from'] ) || version_compare( $version, $data[$slug]['update_from'], '>=' ) )
						&& ( $slug != 'trx_addons' || ! file_exists( $this->theme_dir . '/plugins/trx_addons/trx_addons.zip' ) )
					) {
						$info[$k] = array(
							'title'   => $title,
							'slug'    => $slug,
							'path'    => $path,
							'icon'    => $this->get_item_icon( 'plugin', $slug, $title ),
							'version' => $version,
							'update'  => $data[$slug]['version']
						);
					}
				}
			}
		}
		return apply_filters( 'trx_updater_filter_get_plugins_info', $info );
	}

	/**
	 * Count new plugins
	 *
	 * Return a new plugins number
	 *
	 * @since 1.0.0
	 * @access private
	 */
	private function count_plugin_updates() {
		$update = 0;
		foreach ( $this->plugins_list as $plugin => $data ) {
			if ( ! empty( $data['update'] )
					&& ! empty( $data['version'] )
					&& version_compare( $data['update'], $data['version'], '>' )
					&& ( empty( $data['update_from'] ) || version_compare( $data['version'], $data['update_from'], '>=' ) )
			) {
				$update++;
			}
		}
		return $update;
	}

	/**
	 * Add new plugins count to the WordPress updates count
	 *
	 * Add new plugins count to the WordPress updates count.
	 *
	 * Fired by `wp_get_update_data` action.
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function add_plugins_to_update_counts($update_data, $titles) {
		if ( current_user_can( 'update_plugins' ) ) {
			$update = $this->count_plugin_updates();
			if ( $update > 0 ) {
				$update_data[ 'counts' ][ 'plugins' ] += $update;
				$update_data[ 'counts' ][ 'total' ]   += $update;
				// Translators: %d: number of updates available to installed plugins
				$titles['plugins']                     = sprintf( _n( '%d Plugin Update', '%d Plugin Updates', $update_data[ 'counts' ][ 'plugins' ], 'trx-updater' ), $update );
				$update_data['title']                  = esc_attr( implode( ', ', $titles ) );
			}
		}
		return $update_data;
	}

	/**
	 * Add new plugins versions to the WordPress update screen
	 *
	 * Add new plugins versions to the WordPress update screen
	 *
	 * Fired by `core_upgrade_preamble` action.
	 *
	 * @since 1.0.0
	 * @access public
	 */
	public function add_plugins_to_update_screen() {
		if ( current_user_can( 'update_plugins' ) ) {
			$update = $this->count_plugin_updates();
			if ( $update == 0 ) return;
			?>
			<h2>
				<?php esc_html_e( 'Active theme components: Plugins', 'trx-updater' ); ?>
			</h2>
			<p>
				<?php esc_html_e( 'The following plugins have new versions available. Check the ones you want to update and then click &#8220;Update Plugins&#8221;.', 'trx-updater' ); ?>
			</p>
			<div class="upgrade trx_updater_upgrade trx_updater_upgrade_plugins">
				<p><input id="upgrade-theme-plugins" class="button trx_updater_upgrade_button trx_updater_upgrade_plugins_button" type="button" value="<?php esc_attr_e( 'Update Plugins', 'trx-updater' ); ?>" /></p>
				<table class="widefat updates-table" id="update-plugins-table">
					<thead>
					<tr>
						<td class="manage-column check-column"><input type="checkbox" id="theme-plugins-select-all" /></td>
						<td class="manage-column"><label for="theme-plugins-select-all"><?php esc_html_e( 'Select All', 'trx-updater' ); ?></label></td>
					</tr>
					</thead>
					<tbody class="plugins">
						<?php
						foreach ( $this->plugins_list as $slug => $data ) {
							$plugin = ! empty( $data['path'] ) ? $data['path'] : "{$slug}/{$slug}.php";
							if ( empty( $data['update'] ) || empty( $data['version'] ) || ! version_compare( $data['update'], $data['version'], '>' ) ) {
								continue;
							}
							$checkbox_id = 'checkbox_' . md5( $plugin );
							?>
							<tr>
								<td class="check-column">
									<input type="checkbox"
										name="checked[]"
										id="<?php echo esc_attr( $checkbox_id ); ?>"
										data-update-url="<?php echo esc_url( $this->get_iau_link( $slug, 'update', 'plugin' ) ); ?>"
										<?php if ( $this->check_plugin_state( $slug ) == 'active' ) { ?>
										data-activate-url="<?php echo esc_url( $this->get_iau_link( $slug, 'activate', 'plugin' ) ); ?>"
										<?php } ?>
										value="<?php echo esc_attr( $plugin ); ?>"
									/>
									<label for="<?php echo esc_attr( $checkbox_id ); ?>" class="screen-reader-text">
										<?php
										// Translators: %s: Plugin name
										printf( esc_html__( 'Select %s', 'trx-updater' ), $data['title'] );
										?>
									</label>
								</td>
								<td class="plugin-title"><p>
									<?php echo $data['icon']; ?>
									<strong><?php echo esc_html( $data['title'] ); ?></strong>
									<?php
									// Translators: 1: plugin version, 2: new version
									printf(
										esc_html__( 'You have version %1$s installed. Update to %2$s.', 'trx-updater' ),
										$data['version'],
										$data['update']
									);
									?>
								</p></td>
							</tr>
							<?php
						}
						?>
					</tbody>
					<tfoot>
						<tr>
							<td class="manage-column check-column"><input type="checkbox" id="theme-plugins-select-all-2" /></td>
							<td class="manage-column"><label for="theme-plugins-select-all-2"><?php esc_html_e( 'Select All', 'trx-updater' ); ?></label></td>
						</tr>
					</tfoot>
				</table>
				<p><input id="upgrade-theme-plugins-2" class="button trx_updater_upgrade_button trx_updater_upgrade_plugins_button" type="button" value="<?php esc_attr_e( 'Update Plugins', 'trx-updater' ); ?>" /></p>
			</div>
			<?php
		}
	}

	/**
	 * Prepare plugin to upgrade
	 *
	 * Backup folders before upgrade plugin
	 *
	 * @since 1.4.2
	 * @access public
	 */
	public function before_plugin_upgrade( $options ) {
		if ( ! empty( $options['hook_extra']['plugin'] ) && current_user_can( 'update_plugins' ) ) {
			$plugin = $options['hook_extra']['plugin'];
			$slug   = dirname( $plugin );
			if ( $slug == 'trx_addons' && apply_filters( 'trx_updater_filter_backup_trx_addons', true ) ) {
				// Backup installed addons
				$path       = trailingslashit( $options['destination'] )  . $slug;
				$addons_dir = $path . '/addons';
				if ( is_dir( $addons_dir ) ) {
					$addons_json  = $addons_dir . '/addons.json';
					if ( file_exists( $addons_json ) ) {
						$addons_info = json_decode( trx_updater_fgc( $addons_json ), true );
						$addons_list = glob( $addons_dir . '/*', GLOB_ONLYDIR);
						if ( array( $addons_list ) ) {
							$this->plugins_parts = array( 'addons' => array() );
							trx_updater_allow_upload_archives();
							foreach( $addons_list as $dir ) {
								$name = basename( $dir );
								if ( apply_filters( 'trx_updater_filter_backup_trx_addon', true, $name ) ) {
									$rnd = str_replace('.', '', mt_rand());
									$result = wp_upload_bits( "backup-{$this->theme_slug}-addon-{$name}-{$rnd}.zip", 0, '' );
									if ( ! empty( $result['file'] ) ) {
										if ( trx_updater_pack_archive( $result['file'], $dir ) ) {
											$this->plugins_parts['addons'][$name] = array(
												'backup' => $result['file'],
												'info'   => ! empty( $addons_info[$name] ) ? $addons_info[$name] : ''
											);
										}
									}
								}
							}
							trx_updater_disallow_upload_archives();
						}
					}
				}
			}
			do_action( 'trx_updater_action_before_plugin_upgrade', $slug );
		}
		return $options;
	}

	/**
	 * Restore plugins parts after upgrade
	 *
	 * Restore addons after upgrade plugin trx_addons
	 *
	 * @since 1.4.2
	 * @access public
	 */
	public function after_plugin_upgrade( $actions, $plugin ) {
		if ( ! empty( $plugin ) && current_user_can( 'update_plugins' ) ) {
			$slug = ! empty( $plugin ) && is_string( $plugin ) ? dirname( $plugin ) : '';
			if ( $slug == 'trx_addons' ) {
				// Restore installed addons
				if ( ! empty( $this->plugins_parts['addons'] ) && is_array( $this->plugins_parts['addons'] ) ) {
					$addons_dir  = trailingslashit( dirname( TRX_UPDATER_DIR ) ) . $slug . '/addons';
					if ( is_dir( $addons_dir ) && is_writeable( $addons_dir ) ) {
						$addons_json = $addons_dir . '/addons.json';
						if ( file_exists( $addons_json ) && is_writeable( $addons_json ) ) {
							$addons_info = json_decode( trx_updater_fgc( $addons_json ), true );
							foreach( $this->plugins_parts['addons'] as $addon_name => $addon_data ) {
								$dir = $addons_dir . '/' . trx_updater_esc( $addon_name );
								if ( ! empty( $addon_data['backup'] ) && file_exists( $addon_data['backup'] ) ) {
									if ( ! is_dir( $dir ) || ( ! empty( $addons_info[$addon_name]['version'] ) && ! empty( $addon_data['info']['version'] ) && version_compare( $addons_info[$addon_name]['version'], $addon_data['info']['version'], '<' ) ) ) {
										unzip_file( $addon_data['backup'], $dir );
										if ( ! empty( $addon_data['info'] )
											&& (
												empty( $addons_info[$addon_name] )
												||
												( ! empty( $addons_info[$addon_name]['version'] ) && ! empty( $addon_data['info']['version'] ) && version_compare( $addons_info[$addon_name]['version'], $addon_data['info']['version'], '<' ) )
												)
										) {
											$addons_info[$addon_name] = $addon_data['info'];
										}
									}
									trx_updater_unlink( $addon_data['backup'] );
								}
							}
							trx_updater_fpc( $addons_json, json_encode( $addons_info, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_LINE_TERMINATORS ) );
						}
					}
				}
				// Set flag to regenerate merged styles and scripts on first run
				update_option( 'trx_addons_action', 'trx_addons_action_save_options' );
			}
			do_action( 'trx_updater_action_after_plugin_upgrade', $slug );
		}
		return $actions;
	}


	//---------------------------------- 3-rd Party plugins compatibility ----------------------------

	/**
	 * Plugin's update URL
	 *
	 * Save plugin's update URL (fix for LatePoint)
	 *
	 * @since 1.9.6
	 * @access private
	 *
	 * @var theme_parts
	 */
	private $latepoint_plugin_package;

	/**
	 * LatePoint fix: Save a plugin's package URL
	 *
	 * Save a plugin's package URL
	 * 
	 * @hook 'pre_set_site_transient_update_plugins', 1
	 *
	 * @since 1.9.6
	 * @access public
	 */
	public function fix_latepoint_save_plugin_package( $transient ) {
		if ( ! empty( $transient->response['latepoint/latepoint.php']->package ) ) {
			$this->latepoint_plugin_package = $transient->response['latepoint/latepoint.php']->package;
		}
		return $transient;
	}


	/**
	 * LatePoint fix: Save a plugin's package URL
	 *
	 * Save a plugin's package URL
	 * 
	 * @hook 'pre_set_site_transient_update_plugins', 9999
	 *
	 * @since 1.9.6
	 * @access public
	 */
	public function fix_latepoint_restore_plugin_package( $transient ) {
		if ( ! empty( $transient->response['latepoint/latepoint.php'] )
			&& empty( $transient->response['latepoint/latepoint.php']->package )
			&& ! empty( $this->latepoint_plugin_package )
		) {
			$transient->response['latepoint/latepoint.php']->package = $this->latepoint_plugin_package;
		}
		return $transient;
	}


	/**
	 * Plugin's update URL
	 *
	 * Save plugin's update URL (fix for Slider Revolution)
	 *
	 * @since 1.9.6
	 * @access private
	 *
	 * @var theme_parts
	 */
	private $revslider_plugin_package;
	
	/**
	 * Slider Revolution fix: Save a plugin's package URL
	 *
	 * Save a plugin's package URL
	 *
	 * @hook 'pre_set_site_transient_update_plugins', 1
	 *
	 * @since 1.9.6
	 * @access public
	 */
	public function fix_revslider_save_plugin_package( $transient ) {
		if ( ! empty( $transient->response['revslider/revslider.php']->package ) ) {
			$this->revslider_plugin_package = $transient->response['revslider/revslider.php']->package;
		}
		return $transient;
	}
	
	
	/**
	 * Slider Revolution fix: Save a plugin's package URL
	 *
	 * Save a plugin's package URL
	 *
	 * @hook 'pre_set_site_transient_update_plugins', 9999
	 *
	 * @since 1.9.6
	 * @access public
	 */
	public function fix_revslider_restore_plugin_package( $transient ) {
		if ( ! empty( $transient->response['revslider/revslider.php'] )
				&& empty( $transient->response['revslider/revslider.php']->package )
				&& ! empty( $this->revslider_plugin_package )
		) {
			$transient->response['revslider/revslider.php']->package = $this->revslider_plugin_package;
		}
		return $transient;
	}
	
	
	/**
	 * Disable clear update info for the Booked plugin.
	 * This plugin has a problem while checking for updates from its site and it clears update info
	 * and blocks the update process from our upgrade site.
	 */
	public function fix_booked_disable_clear_update_info() {
		if ( class_exists( 'booked_plugin' ) ) {
			trx_updater_remove_filter( 'site_transient_update_plugins', 'injectUpdate', 'PluginUpdateChecker_2_3' ); //WP 3.0+
			trx_updater_remove_filter( 'transient_update_plugins', 'injectUpdate', 'PluginUpdateChecker_2_3' );      //WP 2.8+
		}
	}
	
}
