441 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			441 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Gitium provides automatic git version control and deployment for
 | |
|  * your plugins and themes integrated into wp-admin.
 | |
|  *
 | |
|  * Copyright (C) 2014-2025 PRESSINFRA SRL <ping@presslabs.com>
 | |
|  *
 | |
|  * Gitium is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * any later version.
 | |
|  *
 | |
|  * Gitium is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with Gitium. If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * @package         Gitium
 | |
|  */
 | |
| 
 | |
| function gitium_error_log( $message ) {
 | |
| 	if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { return; }
 | |
| 	error_log( "gitium_error_log: $message" );
 | |
| }
 | |
| 
 | |
| function wp_content_is_versioned() {
 | |
| 	return file_exists( WP_CONTENT_DIR . '/.git' );
 | |
| }
 | |
| 
 | |
| if ( ! function_exists( 'gitium_enable_maintenance_mode' ) ) :
 | |
| 	function gitium_enable_maintenance_mode() {
 | |
| 		$file = ABSPATH . '/.maintenance';
 | |
| 
 | |
| 		if ( false === file_put_contents( $file, '<?php $upgrading = ' . time() .';' ) ) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| endif;
 | |
| 
 | |
| if ( ! function_exists( 'gitium_disable_maintenance_mode' ) ) :
 | |
| 	function gitium_disable_maintenance_mode() {
 | |
| 		return unlink( ABSPATH . '/.maintenance' );
 | |
| 	}
 | |
| endif;
 | |
| 
 | |
| function gitium_get_versions() {
 | |
| 	$versions = get_transient( 'gitium_versions' );
 | |
| 	if ( empty( $versions ) ) {
 | |
| 		$versions = gitium_update_versions();
 | |
| 	}
 | |
| 	return $versions;
 | |
| }
 | |
| 
 | |
| function _gitium_commit_changes( $message, $dir = '.' ) {
 | |
| 	global $git;
 | |
| 
 | |
| 	list( , $git_private_key ) = gitium_get_keypair();
 | |
| 	if (!$git_private_key)
 | |
| 		return false;
 | |
| 	$git->set_key( $git_private_key );
 | |
| 
 | |
| 	$git->add( $dir );
 | |
| 	gitium_update_versions();
 | |
| 	$current_user = wp_get_current_user();
 | |
| 	return $git->commit( $message, $current_user->display_name, $current_user->user_email );
 | |
| }
 | |
| 
 | |
| function _gitium_format_message( $name, $version = false, $prefix = '' ) {
 | |
| 	$commit_message = "`$name`";
 | |
| 	if ( $version ) {
 | |
| 		$commit_message .= " version $version";
 | |
| 	}
 | |
| 	if ( $prefix ) {
 | |
| 		$commit_message = "$prefix $commit_message";
 | |
| 	}
 | |
| 	return $commit_message;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This function return the basic info about a path.
 | |
|  *
 | |
|  * base_path - means the path after wp-content dir (themes/plugins)
 | |
|  * type      - can be file/theme/plugin
 | |
|  * name      - the file name of the path, if it is a file, or the theme/plugin name
 | |
|  * version   - the theme/plugin version, othewise null
 | |
|  */
 | |
| /* Some examples:
 | |
| 
 | |
|   with 'wp-content/themes/twentyten/style.css' will return:
 | |
|   array(
 | |
|     'base_path' => 'wp-content/themes/twentyten'
 | |
|     'type'      => 'theme'
 | |
|     'name'      => 'TwentyTen'
 | |
|     'version'   => '1.12'
 | |
|   )
 | |
| 
 | |
|   with 'wp-content/themes/twentyten/img/foo.png' will return:
 | |
|   array(
 | |
|     'base_path' => 'wp-content/themes/twentyten'
 | |
|     'type'      => 'theme'
 | |
|     'name'      => 'TwentyTen'
 | |
|     'version'   => '1.12'
 | |
|   )
 | |
| 
 | |
|   with 'wp-content/plugins/foo.php' will return:
 | |
|   array(
 | |
|     'base_path' => 'wp-content/plugins/foo.php'
 | |
|     'type'      => 'plugin'
 | |
|     'name'      => 'Foo'
 | |
|     'varsion'   => '2.0'
 | |
|   )
 | |
| 
 | |
|   with 'wp-content/plugins/autover/autover.php' will return:
 | |
|   array(
 | |
|     'base_path' => 'wp-content/plugins/autover'
 | |
|     'type'      => 'plugin'
 | |
|     'name'      => 'autover'
 | |
|     'version'   => '3.12'
 | |
|   )
 | |
| 
 | |
|   with 'wp-content/plugins/autover/' will return:
 | |
|   array(
 | |
|     'base_path' => 'wp-content/plugins/autover'
 | |
|     'type'      => 'plugin'
 | |
|     'name'      => 'autover'
 | |
|     'version'   => '3.12'
 | |
|   )
 | |
| */
 | |
| function _gitium_module_by_path( $path ) {
 | |
| 	$versions = gitium_get_versions();
 | |
| 
 | |
| 	// default values
 | |
| 	$module   = array(
 | |
| 		'base_path' => $path,
 | |
| 		'type'      => 'file',
 | |
| 		'name'      => basename( $path ),
 | |
| 		'version'   => null,
 | |
| 	);
 | |
| 
 | |
| 	// find the base_path
 | |
| 	$split_path = explode( '/', $path );
 | |
| 	if ( 2 < count( $split_path ) ) {
 | |
| 		$module['base_path'] = "{$split_path[0]}/{$split_path[1]}/{$split_path[2]}";
 | |
| 	}
 | |
| 
 | |
| 	// find other data for theme
 | |
| 	if ( array_key_exists( 'themes', $versions ) && 0 === strpos( $path, 'wp-content/themes/' ) ) {
 | |
| 		$module['type'] = 'theme';
 | |
| 		foreach ( $versions['themes'] as $theme => $data ) {
 | |
| 			if ( 0 === strpos( $path, "wp-content/themes/$theme" ) ) {
 | |
| 				$module['name']    = $data['name'];
 | |
| 				$module['version'] = $data['version'];
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// find other data for plugin
 | |
| 	if ( array_key_exists( 'plugins', $versions ) && 0 === strpos( $path, 'wp-content/plugins/' ) ) {
 | |
| 		$module['type'] = 'plugin';
 | |
| 		foreach ( $versions['plugins'] as $plugin => $data ) {
 | |
| 			if ( '.' === dirname( $plugin ) ) { // single file plugin
 | |
| 				if ( "wp-content/plugins/$plugin" === $path ) {
 | |
| 					$module['base_path'] = $path;
 | |
| 					$module['name']      = $data['name'];
 | |
| 					$module['version']   = $data['version'];
 | |
| 					break;
 | |
| 				}
 | |
| 			} else if ( 'wp-content/plugins/' . dirname( $plugin ) === $module['base_path'] ) {
 | |
| 				$module['name']    = $data['name'];
 | |
| 				$module['version'] = $data['version'];
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return $module;
 | |
| }
 | |
| 
 | |
| function gitium_group_commit_modified_plugins_and_themes( $msg_append = '' ) {
 | |
| 	global $git;
 | |
| 
 | |
| 	$uncommited_changes = $git->get_local_changes();
 | |
| 	$commit_groups = array();
 | |
| 	$commits = array();
 | |
| 
 | |
| 	if ( ! empty( $msg_append ) ) {
 | |
| 		$msg_append = "($msg_append)";
 | |
| 	}
 | |
| 	foreach ( $uncommited_changes as $path => $action ) {
 | |
| 		$change = _gitium_module_by_path( $path );
 | |
| 		$change['action'] = $action;
 | |
| 		$commit_groups[ $change['base_path'] ] = $change;
 | |
| 	}
 | |
| 
 | |
| 	foreach ( $commit_groups as $base_path => $change ) {
 | |
| 		$commit_message = _gitium_format_message( $change['name'], $change['version'], "{$change['action']} {$change['type']}" );
 | |
| 		$commit = _gitium_commit_changes( "$commit_message $msg_append", $base_path, false );
 | |
| 		if ( $commit ) {
 | |
| 			$commits[] = $commit;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return $commits;
 | |
| }
 | |
| 
 | |
| function gitium_commit_and_push_gitignore_file( $path = '' ) {
 | |
| 	global $git;
 | |
| 
 | |
| 	$current_user = wp_get_current_user();
 | |
| 	if ( ! empty( $path ) ) { $git->rm_cached( $path ); }
 | |
| 	$git->add( '.gitignore' );
 | |
| 	$commit = $git->commit( 'Update the `.gitignore` file', $current_user->display_name, $current_user->user_email );
 | |
| 	gitium_merge_and_push( $commit );
 | |
| }
 | |
| 
 | |
| if ( ! function_exists( 'gitium_acquire_merge_lock' ) ) :
 | |
| 	function gitium_acquire_merge_lock() {
 | |
| 		$gitium_lock_path   = apply_filters( 'gitium_lock_path', sys_get_temp_dir().'/.gitium-lock' );
 | |
| 		$gitium_lock_handle = fopen( $gitium_lock_path, 'w+' );
 | |
| 
 | |
| 		$lock_timeout    = intval( ini_get( 'max_execution_time' ) ) > 10 ? intval( ini_get( 'max_execution_time' ) ) - 5 : 10;
 | |
| 		$lock_timeout_ms = 10;
 | |
| 		$lock_retries    = 0;
 | |
| 		while ( ! flock( $gitium_lock_handle, LOCK_EX | LOCK_NB ) ) {
 | |
| 			usleep( $lock_timeout_ms * 1000 );
 | |
| 			$lock_retries++;
 | |
| 			if ( $lock_retries * $lock_timeout_ms > $lock_timeout * 1000 ) {
 | |
| 				return false; // timeout
 | |
| 			}
 | |
| 		}
 | |
| 		gitium_error_log( __FUNCTION__ );
 | |
| 		return array( $gitium_lock_path, $gitium_lock_handle );
 | |
| 	}
 | |
| endif;
 | |
| 
 | |
| if ( ! function_exists( 'gitium_release_merge_lock' ) ) :
 | |
| 	function gitium_release_merge_lock( $lock ) {
 | |
| 		list( $gitium_lock_path, $gitium_lock_handle ) = $lock;
 | |
| 		gitium_error_log( __FUNCTION__ );
 | |
| 		flock( $gitium_lock_handle, LOCK_UN );
 | |
| 		fclose( $gitium_lock_handle );
 | |
| 	}
 | |
| endif;
 | |
| 
 | |
| // Merges the commits with remote and pushes them back
 | |
| function gitium_merge_and_push( $commits ) {
 | |
| 	global $git;
 | |
| 
 | |
| 	$lock = gitium_acquire_merge_lock()
 | |
| 		or trigger_error( 'Timeout when gitium lock was acquired', E_USER_WARNING );
 | |
| 
 | |
| 	if ( ! $git->fetch_ref() ) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	$merge_status = $git->merge_with_accept_mine( $commits );
 | |
| 
 | |
| 	gitium_release_merge_lock( $lock );
 | |
| 
 | |
| 	return $git->push() && $merge_status;
 | |
| }
 | |
| 
 | |
| function gitium_check_after_event( $plugin, $event = 'activation' ) {
 | |
| 	global $git;
 | |
| 
 | |
| 	if ( 'gitium/gitium.php' == $plugin ) { return; } // do not hook on activation of this plugin
 | |
| 
 | |
| 	if ( $git->is_dirty() ) {
 | |
| 		$versions = gitium_update_versions();
 | |
| 		if ( isset( $versions['plugins'][ $plugin ] ) ) {
 | |
| 			$name    = $versions['plugins'][ $plugin ]['name'];
 | |
| 			$version = $versions['plugins'][ $plugin ]['version'];
 | |
| 		} else {
 | |
| 			$name = $plugin;
 | |
| 		}
 | |
| 		gitium_auto_push( _gitium_format_message( $name, $version, "after $event of" ) );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function gitium_update_remote_tracking_branch() {
 | |
| 	global $git;
 | |
| 	$remote_branch = $git->get_remote_tracking_branch();
 | |
| 	set_transient( 'gitium_remote_tracking_branch', $remote_branch );
 | |
| 
 | |
| 	return $remote_branch;
 | |
| }
 | |
| 
 | |
| function _gitium_get_remote_tracking_branch( $update_transient = false ) {
 | |
| 	if ( ! $update_transient && ( false !== ( $remote_branch = get_transient( 'gitium_remote_tracking_branch' ) ) ) ) {
 | |
| 		return $remote_branch;
 | |
| 	} else {
 | |
| 		return gitium_update_remote_tracking_branch();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function gitium_update_is_status_working() {
 | |
| 	global $git;
 | |
| 	$is_status_working = $git->is_status_working();
 | |
| 	set_transient( 'gitium_is_status_working', $is_status_working );
 | |
| 	return $is_status_working;
 | |
| }
 | |
| 
 | |
| function _gitium_is_status_working( $update_transient = false ) {
 | |
| 	if ( ! $update_transient && ( false !== ( $is_status_working = get_transient( 'gitium_is_status_working' ) ) ) ) {
 | |
| 		return $is_status_working;
 | |
| 	} else {
 | |
| 		return gitium_update_is_status_working();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function _gitium_status( $update_transient = false ) {
 | |
| 	global $git;
 | |
| 
 | |
| 	if ( ! $update_transient && ( false !== ( $changes = get_transient( 'gitium_uncommited_changes' ) ) ) ) {
 | |
| 		return $changes;
 | |
| 	}
 | |
| 
 | |
| 	$git_version = get_transient( 'gitium_git_version' );
 | |
| 	if ( false === $git_version ) {
 | |
| 		set_transient( 'gitium_git_version', $git->get_version() );
 | |
| 	}
 | |
| 
 | |
| 	if ( $git->is_status_working() && $git->get_remote_tracking_branch() ) {
 | |
| 		if ( ! $git->fetch_ref() ) {
 | |
| 			set_transient( 'gitium_remote_disconnected', $git->get_last_error() );
 | |
| 		} else {
 | |
| 			delete_transient( 'gitium_remote_disconnected' );
 | |
| 		}
 | |
| 		$changes = $git->status();
 | |
| 	} else {
 | |
| 		delete_transient( 'gitium_remote_disconnected' );
 | |
| 		$changes = array();
 | |
| 	}
 | |
| 
 | |
| 	set_transient( 'gitium_uncommited_changes', $changes, 12 * 60 * 60 ); // cache changes for half-a-day
 | |
| 	return $changes;
 | |
| }
 | |
| 
 | |
| function _gitium_ssh_encode_buffer( $buffer ) {
 | |
| 	$len = strlen( $buffer );
 | |
| 	if ( ord( $buffer[0] ) & 0x80 ) {
 | |
| 		$len++;
 | |
| 		$buffer = "\x00" . $buffer;
 | |
| 	}
 | |
| 	return pack( 'Na*', $len, $buffer );
 | |
| }
 | |
| 
 | |
| function _gitium_generate_keypair() {
 | |
| 	$rsa_key = openssl_pkey_new(
 | |
| 		array(
 | |
| 			'private_key_bits' => 2048,
 | |
| 			'private_key_type' => OPENSSL_KEYTYPE_RSA,
 | |
| 		)
 | |
| 	);
 | |
| 
 | |
| 	try {
 | |
| 		$private_key = openssl_pkey_get_private( $rsa_key );
 | |
| 		$try = openssl_pkey_export( $private_key, $pem ); //Private Key
 | |
| 		if (!$try)
 | |
| 			return false;
 | |
| 	} catch (Exception $e) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	$key_info   = openssl_pkey_get_details( $rsa_key );
 | |
| 	$buffer     = pack( 'N', 7 ) . 'ssh-rsa' .
 | |
| 					_gitium_ssh_encode_buffer( $key_info['rsa']['e'] ) .
 | |
| 					_gitium_ssh_encode_buffer( $key_info['rsa']['n'] );
 | |
| 	$public_key = 'ssh-rsa ' . base64_encode( $buffer ) . ' gitium@' . parse_url( get_home_url(), PHP_URL_HOST );
 | |
| 
 | |
| 	return array( $public_key, $pem );
 | |
| }
 | |
| 
 | |
| function gitium_get_keypair( $generate_new_keypair = false ) {
 | |
| 	if ( $generate_new_keypair ) {
 | |
| 		$keypair = _gitium_generate_keypair();
 | |
| 		delete_option( 'gitium_keypair' );
 | |
| 		add_option( 'gitium_keypair', $keypair, '', false );
 | |
| 	}
 | |
| 	if ( false === ( $keypair = get_option( 'gitium_keypair', false ) ) ) {
 | |
| 		$keypair = _gitium_generate_keypair();
 | |
| 		add_option( 'gitium_keypair', $keypair, '', false );
 | |
| 	}
 | |
| 	return $keypair;
 | |
| }
 | |
| 
 | |
| function _gitium_generate_webhook_key() {
 | |
| 	return md5( str_shuffle( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.()[]{}-_=+!@#%^&*~<>:;' ) );
 | |
| }
 | |
| 
 | |
| function gitium_get_webhook_key( $generate_new_webhook_key = false ) {
 | |
| 	if ( $generate_new_webhook_key ) {
 | |
| 		$key = _gitium_generate_webhook_key();
 | |
| 		delete_option( 'gitium_webhook_key' );
 | |
| 		add_option( 'gitium_webhook_key', $key, '', false );
 | |
| 		return $key;
 | |
| 	}
 | |
| 	if ( false === ( $key = get_option( 'gitium_webhook_key', false ) ) ) {
 | |
| 		$key = _gitium_generate_webhook_key();
 | |
| 		add_option( 'gitium_webhook_key', $key, '', false );
 | |
| 	}
 | |
| 	return $key;
 | |
| }
 | |
| 
 | |
| function gitium_get_webhook() {
 | |
| 	if ( defined( 'GIT_WEBHOOK_URL' ) && GIT_WEBHOOK_URL ) { return GIT_WEBHOOK_URL; }
 | |
| 	$key = gitium_get_webhook_key();
 | |
| 	$url = add_query_arg( 'key', $key, plugins_url( 'gitium-webhook.php', __FILE__ ) );
 | |
| 	return apply_filters( 'gitium_webhook_url', $url, $key );
 | |
| }
 | |
| 
 | |
| function gitium_admin_init() {
 | |
| 	global $git;
 | |
| 
 | |
| 	$git_version = get_transient( 'gitium_git_version' );
 | |
| 	if ( false === $git_version ) {
 | |
| 		set_transient( 'gitium_git_version', $git->get_version() );
 | |
| 	}
 | |
| }
 | |
| add_action( 'admin_init', 'gitium_admin_init' );
 | |
| 
 | |
| add_action('admin_enqueue_scripts', 'enqueue_script_for_gitium_page');
 | |
| function enqueue_script_for_gitium_page($hook) {
 | |
|     // Check if the current page is your plugin's settings page
 | |
|     if ((isset($_GET['page']) && $_GET['page'] === 'gitium/gitium.php') || (isset($_GET['page']) && $_GET['page'] === 'gitium/gitium-settings.php')) {
 | |
|         // Enqueue your JavaScript file
 | |
|         wp_enqueue_script(
 | |
|             'my-plugin-script',  // Handle for the script
 | |
|             plugin_dir_url(__FILE__) . 'js/copy-to-clipboard.js',  // URL to the script
 | |
|             array('jquery'),  // Dependencies
 | |
|             '1.1',  // Version number 
 | |
|             true    // Load in footer
 | |
|         );
 | |
|     }
 | |
| } |