diff --git a/README.md b/README.md index a0616d7..b02ea13 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,12 @@ This plugin is designed for: ## Features -- **Multi-Project Support**: Save and manage multiple GitHub projects in one place -- **Project Cards**: Visual card-based interface showing all your saved projects -- **One-Click Sync**: Synchronize any saved project to pull the latest version from Git - Install plugins directly from public or private GitHub repositories - Update existing plugins installed from GitHub - Preview repository contents before installation - Select specific versions (tags) of a plugin to install - Support for private repositories using GitHub Personal Access Tokens - Automatic plugin folder naming based on the repository name -- Track last synchronization time for each project ## Installation @@ -33,33 +29,12 @@ This plugin is designed for: ## Usage -### Installing a New Plugin - 1. In the WordPress admin panel, go to Plugins > GitHub Installer. -2. Enter a project name (optional) to save the project for later use. -3. Enter the GitHub repository URL of the plugin you want to install. -4. If it's a private repository, check the "Private Repository?" box and enter your GitHub Personal Access Token. -5. The plugin will fetch available versions and provide a preview of the repository contents. -6. Select the version you want to install from the dropdown menu. -7. Click "Install/Update Plugin" to proceed with the installation or update. -8. Optionally, click "Als Projekt speichern" (Save as Project) to save this configuration for future use. - -### Managing Saved Projects - -Once you've saved projects, they will appear as cards below the installation form: - -- **View Project Details**: Each card shows the project name, repository URL, version, privacy status, and last sync time. -- **Synchronize**: Click the "Synchronisieren" button to pull the latest version from Git for that specific project. -- **Delete**: Click the "Löschen" button to remove a saved project from your list (this won't uninstall the plugin, just removes it from the saved projects). - -### Synchronizing Projects - -The synchronization feature allows you to quickly update any saved project: - -1. Find the project card you want to sync. -2. Click the "Synchronisieren" (Synchronize) button. -3. The plugin will automatically fetch the latest version from Git and update the plugin. -4. You'll see a status message indicating success or failure. +2. Enter the GitHub repository URL of the plugin you want to install. +3. If it's a private repository, check the "Private Repository?" box and enter your GitHub Personal Access Token. +4. The plugin will fetch available versions and provide a preview of the repository contents. +5. Select the version you want to install from the dropdown menu. +6. Click "Install/Update Plugin" to proceed with the installation or update. ## Requirements @@ -82,10 +57,8 @@ No additional configuration is required after installation. However, for private ## Security Considerations - The plugin uses nonces and capability checks to ensure only authorized users can install plugins. -- **Personal Access Tokens are stored in the WordPress database** when you save a project. Ensure your WordPress installation is secure. -- Only users with the `manage_options` capability (typically administrators) can access the plugin. +- Personal Access Tokens are not stored by the plugin and must be entered each time for private repositories. - Always review the contents of a repository before installing to ensure it's from a trusted source. -- Saved projects and their tokens are stored using WordPress options API with proper sanitization. ## Limitations diff --git a/advanced-github-plugin-installer.php b/advanced-github-plugin-installer.php index ba9fb8d..95552d0 100644 --- a/advanced-github-plugin-installer.php +++ b/advanced-github-plugin-installer.php @@ -1,8 +1,8 @@ $project_id, - 'name' => sanitize_text_field($project_data['name']), - 'repo_url' => esc_url_raw($project_data['repo_url']), - 'is_private' => (bool) $project_data['is_private'], - 'access_token' => !empty($project_data['access_token']) ? sanitize_text_field($project_data['access_token']) : '', - 'version' => sanitize_text_field($project_data['version']), - 'last_synced' => current_time('mysql') - ); - update_option('github_installer_projects', $projects); - return $project_id; -} - -function delete_project($project_id) { - $projects = get_saved_projects(); - if (isset($projects[$project_id])) { - unset($projects[$project_id]); - update_option('github_installer_projects', $projects); - return true; - } - return false; -} - function github_plugin_installer_page() { if (!current_user_can('manage_options')) { return; @@ -73,201 +38,46 @@ function github_plugin_installer_page() { $is_private = isset($_POST['is_private']) ? true : false; $access_token = $is_private ? sanitize_text_field($_POST['access_token']) : ''; $selected_version = sanitize_text_field($_POST['version']); - + install_update_github_plugin($repo_url, $access_token, $selected_version); } - $saved_projects = get_saved_projects(); - ?> -

- -
-

Neues Projekt hinzufügen

-
- - - - - - - - - - - - - - - - - - - - - -
-
- - -
- +
+ + + + + + + + + + + + + + + + + +
+
+ +
+ - - -

Gespeicherte Projekte

-
- -
-

-
-

Repository:

-

Version:

-

Status:

-

Zuletzt synchronisiert:

-
-
- - -
-
-
- -
- -
-

Keine gespeicherten Projekte vorhanden. Fügen Sie oben ein neues Projekt hinzu.

-
-
&1"; - exec($set_url_command, $url_output, $url_return); - } + $update_command = "cd " . escapeshellarg($plugin_dir) . " && git fetch --all && git checkout " . escapeshellarg($selected_version); + exec($update_command, $output, $return_var); - // Validate version before checkout - if (!empty($selected_version)) { - $update_command = "cd " . escapeshellarg($plugin_dir) . " && git fetch --all && git checkout " . escapeshellarg($selected_version) . " 2>&1"; - exec($update_command, $output, $return_var); - - if ($return_var !== 0) { - // Reset remote URL to original (without token) for security - if (!empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); - } - wp_die('Failed to update the plugin. Error: ' . implode("\n", $output)); - } - } else { - // If no version specified, just fetch and pull the default branch - $update_command = "cd " . escapeshellarg($plugin_dir) . " && git fetch --all && git pull 2>&1"; - exec($update_command, $output, $return_var); - - if ($return_var !== 0) { - // Reset remote URL to original (without token) for security - if (!empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); - } - wp_die('Failed to update the plugin. Error: ' . implode("\n", $output)); - } - } - - // Reset remote URL to original (without token) for security - if (!empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); + if ($return_var !== 0) { + wp_die('Failed to update the plugin. Error: ' . implode("\n", $output)); } } else { // Install new plugin $clone_command = "git clone "; if (!empty($access_token)) { - $repo_url_with_token = str_replace('https://', "https://{$access_token}@", $repo_url); - $clone_command .= escapeshellarg($repo_url_with_token) . " " . escapeshellarg($plugin_dir); - } else { - $clone_command .= escapeshellarg($repo_url) . " " . escapeshellarg($plugin_dir); + $repo_url = str_replace('https://', "https://{$access_token}@", $repo_url); } + $clone_command .= escapeshellarg($repo_url) . " " . escapeshellarg($plugin_dir); exec($clone_command, $output, $return_var); @@ -341,12 +115,6 @@ function install_update_github_plugin($repo_url, $access_token, $selected_versio wp_die('Failed to clone the repository. Error: ' . implode("\n", $output)); } - // Reset remote URL to original (without token) for security - if (!empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); - } - // Checkout the selected version if (!empty($selected_version)) { $checkout_command = "cd " . escapeshellarg($plugin_dir) . " && git checkout " . escapeshellarg($selected_version); @@ -506,124 +274,4 @@ function check_plugin_status() { } else { wp_send_json_success(array('status' => 'not_installed')); } -} - -function save_github_project() { - check_ajax_referer('github_installer_nonce', 'nonce'); - - if (!current_user_can('manage_options')) { - wp_send_json_error('Insufficient permissions.'); - } - - $project_data = array( - 'name' => sanitize_text_field($_POST['name']), - 'repo_url' => esc_url_raw($_POST['repo_url']), - 'is_private' => !empty($_POST['is_private']) && $_POST['is_private'] !== 'false' && $_POST['is_private'] !== '0', - 'access_token' => isset($_POST['access_token']) ? sanitize_text_field($_POST['access_token']) : '', - 'version' => sanitize_text_field($_POST['version']) - ); - - if (empty($project_data['name']) || empty($project_data['repo_url'])) { - wp_send_json_error('Name und Repository URL sind erforderlich.'); - } - - $project_id = save_project($project_data); - wp_send_json_success(array('message' => 'Projekt erfolgreich gespeichert!', 'project_id' => $project_id)); -} - -function delete_github_project() { - check_ajax_referer('github_installer_nonce', 'nonce'); - - if (!current_user_can('manage_options')) { - wp_send_json_error('Insufficient permissions.'); - } - - $project_id = sanitize_text_field($_POST['project_id']); - - if (delete_project($project_id)) { - wp_send_json_success('Projekt erfolgreich gelöscht!'); - } else { - wp_send_json_error('Projekt konnte nicht gelöscht werden.'); - } -} - -function sync_github_project() { - check_ajax_referer('github_installer_nonce', 'nonce'); - - if (!current_user_can('manage_options')) { - wp_send_json_error('Insufficient permissions.'); - } - - $project_id = sanitize_text_field($_POST['project_id']); - $projects = get_saved_projects(); - - if (!isset($projects[$project_id])) { - wp_send_json_error('Projekt nicht gefunden.'); - } - - $project = $projects[$project_id]; - - // Perform the sync - try { - $repo_url = $project['repo_url']; - $access_token = $project['access_token']; - $version = $project['version']; - - $repo_name = strtolower(basename(parse_url($repo_url, PHP_URL_PATH), '.git')); - $plugin_dir = WP_PLUGIN_DIR . '/' . $repo_name; - - if (!file_exists($plugin_dir)) { - wp_send_json_error('Plugin ist nicht installiert. Bitte installieren Sie es zuerst über das Formular oben.'); - } - - // Update remote URL with access token if private repository - if ($project['is_private'] && !empty($access_token)) { - $auth_repo_url = str_replace('https://', "https://{$access_token}@", $repo_url); - $set_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($auth_repo_url) . " 2>&1"; - exec($set_url_command, $url_output, $url_return); - } - - // Update existing plugin - validate version before checkout - if (!empty($version)) { - $update_command = "cd " . escapeshellarg($plugin_dir) . " && git fetch --all && git checkout " . escapeshellarg($version) . " 2>&1"; - exec($update_command, $output, $return_var); - - if ($return_var !== 0) { - // Reset remote URL to original (without token) for security - if ($project['is_private'] && !empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); - } - wp_send_json_error('Synchronisierung fehlgeschlagen: ' . implode("\n", $output)); - } - } else { - // If no version specified, just fetch and pull the default branch - $update_command = "cd " . escapeshellarg($plugin_dir) . " && git fetch --all && git pull 2>&1"; - exec($update_command, $output, $return_var); - - if ($return_var !== 0) { - // Reset remote URL to original (without token) for security - if ($project['is_private'] && !empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); - } - wp_send_json_error('Synchronisierung fehlgeschlagen: ' . implode("\n", $output)); - } - } - - // Reset remote URL to original (without token) for security - if ($project['is_private'] && !empty($access_token)) { - $reset_url_command = "cd " . escapeshellarg($plugin_dir) . " && git remote set-url origin " . escapeshellarg($repo_url) . " 2>&1"; - exec($reset_url_command, $reset_output, $reset_return); - } - - // Update last_synced timestamp - $project['last_synced'] = current_time('mysql'); - $projects[$project_id] = $project; - update_option('github_installer_projects', $projects); - - wp_send_json_success('Projekt erfolgreich synchronisiert!'); - } catch (Exception $e) { - wp_send_json_error('Fehler bei der Synchronisierung: ' . $e->getMessage()); - } } \ No newline at end of file diff --git a/installer-script.js b/installer-script.js index 30a4f97..d47614d 100644 --- a/installer-script.js +++ b/installer-script.js @@ -17,15 +17,6 @@ jQuery(document).ready(function() { previewTimer = setTimeout(updatePreviewAndVersions, 500); }); - // Show "Save as Project" button when version is selected - jQuery('#version').on('change', function() { - if(jQuery(this).val()) { - jQuery('#save_project_btn').show(); - } else { - jQuery('#save_project_btn').hide(); - } - }); - function updatePreviewAndVersions() { previewRepo(); getVersions(); @@ -87,137 +78,22 @@ jQuery(document).ready(function() { var versions = response.data; var versionSelect = jQuery('#version'); versionSelect.empty(); - versionSelect.append(jQuery('').attr('value', '').text('-- Wählen Sie eine Version --')); jQuery.each(versions, function(index, version) { versionSelect.append(jQuery('').attr('value', version).text(version)); }); jQuery('#version_row').show(); } else { jQuery('#version_row').hide(); - jQuery('#save_project_btn').hide(); console.error('Failed to fetch versions:', response.data); } }, error: function() { jQuery('#version_row').hide(); - jQuery('#save_project_btn').hide(); console.error('An error occurred while fetching the repository versions.'); } }); } else { jQuery('#version_row').hide(); - jQuery('#save_project_btn').hide(); } } - - // Save project button handler - jQuery('#save_project_btn').on('click', function(e) { - e.preventDefault(); - - var projectName = jQuery('#project_name').val(); - var repoUrl = jQuery('#repo_url').val(); - var isPrivate = jQuery('#is_private').is(':checked'); - var accessToken = jQuery('#access_token').val(); - var version = jQuery('#version').val(); - - if (!projectName) { - alert('Bitte geben Sie einen Projektnamen ein.'); - return; - } - - if (!repoUrl || !version) { - alert('Bitte füllen Sie alle erforderlichen Felder aus.'); - return; - } - - jQuery.ajax({ - url: github_installer.ajax_url, - type: 'POST', - data: { - action: 'save_github_project', - nonce: github_installer.nonce, - name: projectName, - repo_url: repoUrl, - is_private: isPrivate, - access_token: accessToken, - version: version - }, - success: function(response) { - if (response.success) { - alert(response.data.message); - location.reload(); - } else { - alert('Fehler: ' + response.data); - } - }, - error: function() { - alert('Ein Fehler ist beim Speichern des Projekts aufgetreten.'); - } - }); - }); - - // Sync project button handler - jQuery(document).on('click', '.github-sync-btn', function() { - var projectId = jQuery(this).data('project-id'); - var button = jQuery(this); - var statusDiv = jQuery('.github-sync-status[data-project-id="' + projectId + '"]'); - - button.prop('disabled', true); - statusDiv.removeClass('success error').addClass('loading').text('Synchronisierung läuft...').show(); - - jQuery.ajax({ - url: github_installer.ajax_url, - type: 'POST', - data: { - action: 'sync_github_project', - nonce: github_installer.nonce, - project_id: projectId - }, - success: function(response) { - button.prop('disabled', false); - if (response.success) { - statusDiv.removeClass('loading error').addClass('success').text(response.data); - setTimeout(function() { - location.reload(); - }, 1500); - } else { - statusDiv.removeClass('loading success').addClass('error').text('Fehler: ' + response.data); - } - }, - error: function() { - button.prop('disabled', false); - statusDiv.removeClass('loading success').addClass('error').text('Ein Fehler ist aufgetreten.'); - } - }); - }); - - // Delete project button handler - jQuery(document).on('click', '.github-delete-btn', function() { - var projectId = jQuery(this).data('project-id'); - - if (!confirm('Möchten Sie dieses Projekt wirklich löschen?')) { - return; - } - - jQuery.ajax({ - url: github_installer.ajax_url, - type: 'POST', - data: { - action: 'delete_github_project', - nonce: github_installer.nonce, - project_id: projectId - }, - success: function(response) { - if (response.success) { - alert(response.data); - location.reload(); - } else { - alert('Fehler: ' + response.data); - } - }, - error: function() { - alert('Ein Fehler ist beim Löschen des Projekts aufgetreten.'); - } - }); - }); }); \ No newline at end of file