From 70001df94199c39a4dc44680527823fa12e3bb6f Mon Sep 17 00:00:00 2001 From: Roberto Guagliardo Date: Sun, 1 Feb 2026 03:04:38 +0000 Subject: [PATCH] feat: implement new license validation and updater system with SitiWebUpdater2 --- LICENSE_INTEGRATION.md | 85 +++++++++ SitiWebUpdater.php | 262 -------------------------- groq-ai-product-text.php | 9 +- includes/SitiLicenseValidator.php | 130 +++++++++++++ includes/SitiWebUpdater2.php | 293 ++++++++++++++++++++++++++++++ 5 files changed, 512 insertions(+), 267 deletions(-) create mode 100644 LICENSE_INTEGRATION.md delete mode 100644 SitiWebUpdater.php create mode 100644 includes/SitiLicenseValidator.php create mode 100644 includes/SitiWebUpdater2.php diff --git a/LICENSE_INTEGRATION.md b/LICENSE_INTEGRATION.md new file mode 100644 index 0000000..06240a5 --- /dev/null +++ b/LICENSE_INTEGRATION.md @@ -0,0 +1,85 @@ +# Nieuwe License Validator en Updater + +Deze plugin gebruikt nu een eigen licentie- en updatesysteem via plugins.robert.ooo in plaats van GitHub. + +## Gebruik + +### License Validator + +```php +require_once __DIR__ . '/includes/SitiLicenseValidator.php'; + +// Initialiseer validator +$validator = new SitiLicenseValidator(); + +// Stel licentie in (van settings) +$validator->set_license_key( get_option( 'siti_license_key' ) ); + +// Verificeer +$result = $validator->verify(); +if ( is_wp_error( $result ) ) { + // Toon fout + echo $result->get_error_message(); +} else { + // Licentie geldig + $version = $result['license']['pluginVersion']; +} +``` + +### SitiWebUpdater2 + +```php +require_once __DIR__ . '/includes/SitiWebUpdater2.php'; + +// Initialiseer updater +$updater = new SitiWebUpdater2( __FILE__ ); + +// Stel owner/repo in (bijv. van manifest.json) +$updater->set_owner( 'siti-ai-product-content-generator' ); +$updater->set_repository( 'siti-ai-product-content-generator' ); + +// Stel licentie in voor downloads +$updater->set_license_key( get_option( 'siti_license_key' ) ); + +// API URL (optioneel, default is plugins.robert.ooo) +$updater->set_api_base_url( 'https://plugins.robert.ooo' ); +``` + +## Instellingen + +Voeg in je admin settings pagina velden toe voor: + +- Licentiecode +- API URL (optioneel) + +Sla op in wp_options: + +```php +update_option( 'siti_license_key', sanitize_text_field( $_POST['license_key'] ) ); +update_option( 'siti_api_url', esc_url_raw( $_POST['api_url'] ) ?: 'https://plugins.robert.ooo' ); +``` + +## Cron Job voor Licentie Checks + +```php +add_action( 'siti_daily_license_check', function() { + $validator = new SitiLicenseValidator(); + $result = $validator->verify(); + // Log of handel af +} ); + +if ( ! wp_next_scheduled( 'siti_daily_license_check' ) ) { + wp_schedule_event( time(), 'daily', 'siti_daily_license_check' ); +} +``` + +## Admin Notices + +```php +add_action( 'admin_notices', function() { + $validator = new SitiLicenseValidator(); + if ( ! $validator->is_valid() ) { + echo '

Licentie ongeldig. Controleer je instellingen.

'; + } +} ); +``` \ No newline at end of file diff --git a/SitiWebUpdater.php b/SitiWebUpdater.php deleted file mode 100644 index 5e82566..0000000 --- a/SitiWebUpdater.php +++ /dev/null @@ -1,262 +0,0 @@ -file = $file; - $this->api_base_url = 'https://api.github.com'; - $this->auth_header_scheme = 'bearer'; - $this->download_auth_header_scheme = 'token'; - - $this->set_plugin_properties(); - add_action( 'admin_init', array( $this, 'set_plugin_properties' ) ); - - return $this; - } - - public function set_plugin_properties() { - if ( ! function_exists( 'get_plugin_data' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - } - - $this->plugin = get_plugin_data( $this->file, false, false ); - $this->basename = plugin_basename( $this->file ); - $this->active = is_plugin_active( $this->basename ); - } - - public function set_username( $username ) { - $this->username = $username; - } - - public function set_repository( $repository ) { - $this->repository = $repository; - } - - public function authorize( $token ) { - $this->authorize_token = $token; - } - - public function set_api_base_url( $api_base_url ) { - $this->api_base_url = rtrim( (string) $api_base_url, '/' ); - } - - public function set_auth_header_scheme( $scheme ) { - $this->auth_header_scheme = (string) $scheme; - } - - public function set_download_auth_header_scheme( $scheme ) { - $this->download_auth_header_scheme = (string) $scheme; - } - - private function get_repository_info() { - if ( is_null( $this->github_response ) ) { // Do we have a response? - $args = array(); - $request_uri = sprintf( '%s/repos/%s/%s/releases', $this->api_base_url, $this->username, $this->repository ); // Build URI - - $args = array(); - - if( $this->authorize_token ) { // Is there an access token? - $scheme = trim( $this->auth_header_scheme ); - $scheme = '' === $scheme ? 'bearer' : $scheme; - $args['headers']['Authorization'] = $scheme . ' ' . $this->authorize_token; // Set the headers - } - - $response = json_decode( wp_remote_retrieve_body( wp_remote_get( $request_uri, $args ) ), true ); // Get JSON and parse it - - if( is_array( $response ) ) { // If it is an array - $response = current( $response ); // Get the first item - } - - $this->github_response = $response; // Set it to our property - } - } - - private function get_latest_version_from_response() { - if ( empty( $this->github_response['tag_name'] ) ) { - return null; - } - - return ltrim( $this->github_response['tag_name'], 'vV' ); - } - - public function initialize() { - add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'modify_transient' ), 10, 1 ); - add_filter( 'plugins_api', array( $this, 'plugin_popup' ), 10, 3); - add_filter( 'upgrader_post_install', array( $this, 'after_install' ), 10, 3 ); - - // Add Authorization Token to download_package - add_filter( 'upgrader_pre_download', - function() { - add_filter( 'http_request_args', [ $this, 'download_package' ], 15, 2 ); - return false; // upgrader_pre_download filter default return value. - } - ); - } - - public function modify_transient( $transient ) { - - if( property_exists( $transient, 'checked') ) { // Check if transient has a checked property - - if( $checked = $transient->checked ) { // Did Wordpress check for updates? - - $this->get_repository_info(); // Get the repo info - - $latest_version = $this->get_latest_version_from_response(); - - if ( null === $latest_version ) { - return $transient; - } - - $out_of_date = version_compare( $latest_version, $checked[ $this->basename ], 'gt' ); // Check if we're out of date - - if( $out_of_date ) { - - $new_files = $this->github_response['zipball_url']; // Get the ZIP - - $slug = current( explode('/', $this->basename ) ); // Create valid slug - - $plugin = array( // setup our plugin info - 'url' => $this->plugin["PluginURI"], - 'slug' => $slug, - 'package' => $new_files, - 'new_version' => $latest_version, - 'icons' => $this->prepare_icon_set(), - ); - - $transient->response[$this->basename] = (object) $plugin; // Return it in response - } - } - } - - return $transient; // Return filtered transient - } - - public function plugin_popup( $result, $action, $args ) { - - if( ! empty( $args->slug ) ) { // If there is a slug - - if( $args->slug == current( explode( '/' , $this->basename ) ) ) { // And it's our slug - - $this->get_repository_info(); // Get our repo info - $latest_version = $this->get_latest_version_from_response(); - - if ( null === $latest_version ) { - return $result; - } - - // Set it to an array - $plugin = array( - 'name' => $this->plugin["Name"], - 'slug' => $this->basename, - 'requires' => '5.1', - 'tested' => '6.4.3', - 'rating' => '100.0', - 'num_ratings' => '10823', - 'downloaded' => '14249', - 'added' => '2016-01-05', - 'version' => $latest_version, - 'author' => $this->plugin["AuthorName"], - 'author_profile' => $this->plugin["AuthorURI"], - 'last_updated' => $this->github_response['published_at'], - 'homepage' => $this->plugin["PluginURI"], - 'short_description' => $this->plugin["Description"], - 'sections' => array( - 'description' => wp_kses_post( wpautop( $this->plugin["Description"] ) ), - 'changelog' => $this->get_release_notes_html(), - ), - 'download_link' => $this->github_response['zipball_url'], - 'icons' => $this->prepare_icon_set(), - ); - - return (object) $plugin; // Return the data - } - - } - return $result; // Otherwise return default - } - - public function download_package( $args, $url ) { - - if ( null !== $args['filename'] ) { - if( $this->authorize_token ) { - $scheme = trim( $this->download_auth_header_scheme ); - $scheme = '' === $scheme ? 'token' : $scheme; - $args = array_merge( $args, array( "headers" => array( "Authorization" => $scheme . ' ' . $this->authorize_token ) ) ); - } - } - - remove_filter( 'http_request_args', [ $this, 'download_package' ] ); - - return $args; - } - - public function after_install( $response, $hook_extra, $result ) { - global $wp_filesystem; // Get global FS object - - $install_directory = plugin_dir_path( $this->file ); // Our plugin directory - $wp_filesystem->move( $result['destination'], $install_directory ); // Move files to the plugin dir - $result['destination'] = $install_directory; // Set the destination for the rest of the stack - - if ( $this->active ) { // If it was active - activate_plugin( $this->basename ); // Reactivate - } - - return $result; - } - - private function get_release_notes_html() { - $notes = isset( $this->github_response['body'] ) ? (string) $this->github_response['body'] : ''; - - if ( '' === trim( $notes ) ) { - return __( 'Nog geen changelog beschikbaar.', 'siti-ai-product-content-generator' ); - } - - return wp_kses_post( wpautop( $notes ) ); - } - - private function get_plugin_icon_url() { - $file = plugin_dir_path( $this->file ) . 'assets/images/plugin-icon.svg'; - if ( ! file_exists( $file ) ) { - return ''; - } - - return plugins_url( 'assets/images/plugin-icon.svg', $this->file ); - } - - private function prepare_icon_set() { - $icon_url = $this->get_plugin_icon_url(); - if ( '' === $icon_url ) { - return array(); - } - - return array( - '1x' => $icon_url, - '2x' => $icon_url, - 'svg' => $icon_url, - 'default' => $icon_url, - ); - } -} diff --git a/groq-ai-product-text.php b/groq-ai-product-text.php index 57aa00f..ee33786 100644 --- a/groq-ai-product-text.php +++ b/groq-ai-product-text.php @@ -72,14 +72,13 @@ require_once __DIR__ . '/includes/Admin/class-groq-ai-logs-table.php'; require_once __DIR__ . '/includes/Admin/class-groq-ai-product-ui.php'; require_once __DIR__ . '/includes/Admin/class-groq-ai-settings-renderer.php'; -if( ! class_exists( 'SitiWebUpdater' ) ){ - include_once( plugin_dir_path( __FILE__ ) . 'SitiWebUpdater.php' ); +if( ! class_exists( 'SitiWebUpdater2' ) ){ + include_once( plugin_dir_path( __FILE__ ) . 'includes/SitiWebUpdater2.php' ); } -$updater = new SitiWebUpdater( __FILE__ ); -$updater->set_username( 'SitiWeb' ); +$updater = new SitiWebUpdater2( __FILE__ ); +$updater->set_owner( 'roberto' ); $updater->set_repository( 'siti-ai-product-content-generator' ); -$updater->set_api_base_url( 'https://git.robert.ooo/api/v1' ); $updater->initialize(); diff --git a/includes/SitiLicenseValidator.php b/includes/SitiLicenseValidator.php new file mode 100644 index 0000000..141c186 --- /dev/null +++ b/includes/SitiLicenseValidator.php @@ -0,0 +1,130 @@ +license_key = $license_key ?: get_option( 'siti_license_key' ); + $this->hostname = $hostname ?: parse_url( home_url(), PHP_URL_HOST ); + } + + /** + * Set the license key + */ + public function set_license_key( $key ) { + $this->license_key = $key; + } + + /** + * Set the API base URL + */ + public function set_api_base_url( $url ) { + $this->api_base_url = rtrim( $url, '/' ); + } + + /** + * Set the hostname + */ + public function set_hostname( $hostname ) { + $this->hostname = $hostname; + } + + /** + * Verify the license + * + * @return array|WP_Error License data on success, WP_Error on failure + */ + public function verify() { + if ( empty( $this->license_key ) ) { + return new WP_Error( 'missing_license', 'Geen licentiecode ingesteld.' ); + } + + if ( empty( $this->hostname ) ) { + return new WP_Error( 'missing_hostname', 'Kon hostname niet bepalen.' ); + } + + $response = wp_remote_post( $this->api_base_url . '/api/licenses/verify', array( + 'headers' => array( + 'Content-Type' => 'application/json', + ), + 'body' => wp_json_encode( array( + 'key' => $this->license_key, + 'hostname' => $this->hostname, + ) ), + 'timeout' => 15, + ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = wp_remote_retrieve_response_code( $response ); + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( 200 !== $code ) { + $error_message = isset( $body['error'] ) ? $body['error'] : 'Licentie verificatie mislukt.'; + return new WP_Error( 'verification_failed', $error_message ); + } + + if ( empty( $body['valid'] ) ) { + return new WP_Error( 'invalid_license', 'Licentie is ongeldig.' ); + } + + // Update last check time + update_option( 'siti_license_last_check', current_time( 'mysql' ) ); + + // Store license data + if ( isset( $body['license'] ) ) { + update_option( 'siti_license_data', $body['license'] ); + } + + return $body; + } + + /** + * Get stored license data + */ + public function get_license_data() { + return get_option( 'siti_license_data', array() ); + } + + /** + * Check if license is valid (cached) + */ + public function is_valid() { + $data = $this->get_license_data(); + return ! empty( $data ) && isset( $data['key'] ); + } + + /** + * Get the latest version from license data + */ + public function get_latest_version() { + $data = $this->get_license_data(); + return $data['pluginVersion'] ?? null; + } + + /** + * Download a specific version + * + * @param string $version Version to download (e.g., 'v1.2.3' or 'latest') + * @return string|WP_Error Download URL or error + */ + public function get_download_url( $version = 'latest' ) { + if ( empty( $this->license_key ) || empty( $this->hostname ) ) { + return new WP_Error( 'missing_credentials', 'Licentie of hostname ontbreekt.' ); + } + + // For now, return the API download endpoint + // In practice, you might want to proxy this through WordPress + return $this->api_base_url . '/api/licenses/download?key=' . urlencode( $this->license_key ) . '&hostname=' . urlencode( $this->hostname ) . '&version=' . urlencode( $version ); + } +} \ No newline at end of file diff --git a/includes/SitiWebUpdater2.php b/includes/SitiWebUpdater2.php new file mode 100644 index 0000000..f12bad5 --- /dev/null +++ b/includes/SitiWebUpdater2.php @@ -0,0 +1,293 @@ +file = $file; + $this->option_prefix = 'siti_updater_' . sanitize_key( dirname( plugin_basename( $this->file ) ) ) . '_'; + $this->set_plugin_properties(); + + add_action( 'admin_init', array( $this, 'set_plugin_properties' ) ); + add_action( 'admin_notices', array( $this, 'admin_notices' ) ); + add_action( 'siti_updater_daily_check_' . $this->option_prefix, array( $this, 'daily_license_check' ) ); + + if ( ! wp_next_scheduled( 'siti_updater_daily_check_' . $this->option_prefix ) ) { + wp_schedule_event( time(), 'daily', 'siti_updater_daily_check_' . $this->option_prefix ); + } + + return $this; + } + + public function initialize() { + add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_for_updates' ) ); + add_filter( 'plugins_api', array( $this, 'plugin_info' ), 10, 3 ); + add_action( 'admin_menu', array( $this, 'add_settings_page' ) ); + } + + public function set_plugin_properties() { + if ( ! function_exists( 'get_plugin_data' ) ) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + + $this->plugin = get_plugin_data( $this->file, false, false ); + $this->basename = plugin_basename( $this->file ); + $this->active = is_plugin_active( $this->basename ); + } + + public function set_owner( $owner ) { + $this->owner = $owner; + } + + public function set_repository( $repository ) { + $this->repository = $repository; + } + + public function set_license_key( $key ) { + $this->license_key = $key; + update_option( $this->option_prefix . 'license_key', $key ); + } + + public function get_license_key() { + if ( null === $this->license_key ) { + $this->license_key = get_option( $this->option_prefix . 'license_key', '' ); + } + return $this->license_key; + } + + public function set_api_base_url( $api_base_url ) { + $this->api_base_url = rtrim( (string) $api_base_url, '/' ); + } + + private function get_plugin_info() { + if ( is_null( $this->plugin_response ) ) { + $request_uri = sprintf( '%s/api/plugins/%s/%s', $this->api_base_url, $this->owner, $this->repository ); + + $response = wp_remote_get( $request_uri, array( 'timeout' => 15 ) ); + + if ( is_wp_error( $response ) ) { + $this->plugin_response = false; + return; + } + + $code = wp_remote_retrieve_response_code( $response ); + if ( 200 !== $code ) { + $this->plugin_response = false; + return; + } + + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + $this->plugin_response = $body; + } + } + + public function check_for_updates( $transient ) { + if ( empty( $transient->checked ) ) { + return $transient; + } + + $this->get_plugin_info(); + + if ( false === $this->plugin_response || empty( $this->plugin_response['version'] ) ) { + return $transient; + } + + $remote_version = $this->plugin_response['version']; + $current_version = $this->plugin['Version']; + + if ( version_compare( $remote_version, $current_version, '>' ) ) { + $download_url = $this->get_download_url( 'latest' ); + + if ( ! is_wp_error( $download_url ) ) { + $transient->response[ $this->basename ] = (object) array( + 'slug' => dirname( $this->basename ), + 'new_version' => $remote_version, + 'package' => $download_url, + 'tested' => get_bloginfo( 'version' ), + 'url' => $this->plugin_response['homepage'] ?? '', + ); + } + } + + return $transient; + } + + public function plugin_info( $result, $action, $args ) { + if ( 'plugin_information' !== $action || $args->slug !== dirname( $this->basename ) ) { + return $result; + } + + $this->get_plugin_info(); + + if ( false === $this->plugin_response ) { + return $result; + } + + return (object) array( + 'name' => $this->plugin_response['name'] ?? $this->plugin['Name'], + 'slug' => $args->slug, + 'version' => $this->plugin_response['version'], + 'author' => $this->plugin_response['author'] ?? $this->plugin['Author'], + 'author_profile' => $this->plugin_response['author_url'] ?? $this->plugin['AuthorURI'], + 'homepage' => $this->plugin_response['homepage'] ?? '', + 'requires' => $this->plugin_response['requires'] ?? '', + 'tested' => $this->plugin_response['tested'] ?? get_bloginfo( 'version' ), + 'downloaded' => $this->plugin_response['downloads'] ?? 0, + 'last_updated' => $this->plugin_response['last_updated'] ?? '', + 'sections' => array( + 'description' => $this->plugin_response['description'] ?? $this->plugin['Description'], + 'changelog' => $this->plugin_response['changelog'] ?? '', + ), + 'download_link' => $this->get_download_url( 'latest' ), + ); + } + + private function get_download_url( $version = 'latest' ) { + $license_key = $this->get_license_key(); + if ( empty( $license_key ) ) { + return new WP_Error( 'no_license', 'Geen licentie ingesteld' ); + } + + $hostname = parse_url( home_url(), PHP_URL_HOST ); + return $this->api_base_url . '/api/licenses/download?key=' . urlencode( $license_key ) . '&hostname=' . urlencode( $hostname ) . '&version=' . urlencode( $version ); + } + + public function verify_license() { + $license_key = $this->get_license_key(); + if ( empty( $license_key ) ) { + return new WP_Error( 'missing_license', 'Geen licentiecode ingesteld.' ); + } + + $hostname = parse_url( home_url(), PHP_URL_HOST ); + $response = wp_remote_post( $this->api_base_url . '/api/licenses/verify', array( + 'headers' => array( + 'Content-Type' => 'application/json', + ), + 'body' => wp_json_encode( array( + 'key' => $license_key, + 'hostname' => $hostname, + ) ), + 'timeout' => 15, + ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = wp_remote_retrieve_response_code( $response ); + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( 200 !== $code ) { + $error_message = isset( $body['error'] ) ? $body['error'] : 'Licentie verificatie mislukt.'; + return new WP_Error( 'verification_failed', $error_message ); + } + + if ( empty( $body['valid'] ) ) { + return new WP_Error( 'invalid_license', 'Licentie is ongeldig.' ); + } + + // Store license data + update_option( $this->option_prefix . 'license_data', $body['license'] ); + update_option( $this->option_prefix . 'last_check', current_time( 'mysql' ) ); + + return $body; + } + + public function is_license_valid() { + $data = get_option( $this->option_prefix . 'license_data', array() ); + return ! empty( $data ) && isset( $data['key'] ); + } + + public function admin_notices() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + if ( ! $this->is_license_valid() ) { + $settings_url = admin_url( 'options-general.php?page=siti-updater-' . sanitize_title( $this->plugin['Name'] ) ); + echo '

'; + printf( + esc_html__( '%s: Licentie ongeldig of niet ingesteld. Ga naar %s om je licentie in te voeren.', 'siti-updater' ), + esc_html( $this->plugin['Name'] ), + '' . esc_html__( 'instellingen', 'siti-updater' ) . '' + ); + echo '

'; + } + } + + public function daily_license_check() { + $result = $this->verify_license(); + if ( is_wp_error( $result ) ) { + error_log( $this->plugin['Name'] . ' License check failed: ' . $result->get_error_message() ); + } + } + + public function add_settings_page() { + add_options_page( + sprintf( __( '%s Licentie', 'siti-updater' ), $this->plugin['Name'] ), + sprintf( __( '%s Licentie', 'siti-updater' ), $this->plugin['Name'] ), + 'manage_options', + 'siti-updater-' . sanitize_title( $this->plugin['Name'] ), + array( $this, 'render_settings_page' ) + ); + } + + public function render_settings_page() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + if ( isset( $_POST['siti_updater_license_key'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'siti_updater_save' ) ) { + $this->set_license_key( sanitize_text_field( $_POST['siti_updater_license_key'] ) ); + $result = $this->verify_license(); + if ( is_wp_error( $result ) ) { + add_settings_error( 'siti_updater', 'license_error', $result->get_error_message() ); + } else { + add_settings_error( 'siti_updater', 'license_success', __( 'Licentie succesvol opgeslagen en geverifieerd.', 'siti-updater' ), 'updated' ); + } + } + + ?> +
+

plugin['Name'] ); ?>

+

+ plugin['Name'] ); ?> +

+ + + +
+ + + + + + +
+ +

+
+

+ +

+
+
+