# booktics/1.0.29/core/extensions/controllers/module-controller.php

Booktics – Appointment Booking Calendar for Service Businesses, version 1.0.29. 399 lines.

- Page: https://pluginprobe.com/plugins/booktics/1.0.29/code/core/extensions/controllers/module-controller.php
- Raw: https://pluginprobe.com/plugins/booktics/1.0.29/raw/core/extensions/controllers/module-controller.php
- Modified: 2026-10-11T10:01:32+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booktics/1.0.29/code/core/extensions/controllers/module-controller.php#L10-L20`.

```php
<?php

namespace Booktics\Extensions\Controllers;

use Booktics\Abstracts\Base_Rest_Controller;
use Arraytics\ToolsSdk\Extension as ExtensionManager;
use Arraytics\ToolsSdk\PluginManager;
use WP_HTTP_Response;
use WP_REST_Request;

/**
 * Module Controller
 *
 * @package Booktics/Module
 */
class Module_Controller extends Base_Rest_Controller {
    /**
     * The namespace of this controller's route.
     *
     * @since 1.0.0
     * @var string
     */
    protected $namespace = 'booktics/v1';

    /**
     * The base of this controller's route.
     *
     * @since 1.0.0
     * @var string
     */
    protected $rest_base = 'module';

    /**
     * Extension manager instance
     *
     * @var ExtensionManager
     */
    protected $extensions;

    /**
     * Initialize the class and set its properties.
     */
    public function __construct() {
        $this->extensions        = new ExtensionManager('booktics_available_modules', booktics_modules_list());
        $this->plugin_manager    = new PluginManager();
    }

    /**
     * Register the routes for the objects of the controller.
     *
     * @since 1.0.0
     *
     * @return void
     */
    public function register_routes(): void {
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods'             => \WP_REST_Server::READABLE,
                'callback'            => [$this, 'get_items'],
                'permission_callback' => [$this, 'get_items_permissions_check'],
                'args'                => [
                    'type' => [
                        'description' => __('Type of extensions to retrieve (module, addon, plugin, all).', 'booktics'),
                        'type'        => 'string',
                        'enum'        => ['module', 'addon', 'plugin', 'all'],
                        'default'     => 'all',
                    ],
                ],
            ],
            [
                'methods'             => \WP_REST_Server::EDITABLE,
                'callback'            => [$this, 'update_item'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
            ],
        ]);
    }

    /**
     * Check if a given request has permission to get items.
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool True if the request has read access, false otherwise.
     */
    public function get_items_permissions_check($request) {
        return current_user_can('manage_options');
    }

    /**
     * Check if a given request has permission to update items.
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool True if the request has update access, false otherwise.
     */
    public function update_item_permissions_check($request) {
        return current_user_can('manage_options');
    }

    /**
     * Get all extensions
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_HTTP_Response Response object on success, or WP_Error object on failure.
     */
    public function get_items($request) {
        $type = ! empty( $request['type'] ) ? sanitize_key( $request['type'] ) : 'all';

        $types = [
            'module' => $this->extensions->get_modules(),
            'addon'  => $this->extensions->get_addons(),
            'plugin' => $this->extensions->get_plugins(),
            'all'    => $this->extensions->get()
        ];

        if (!isset($types[$type])) {
            return $this->error(
                __('Invalid extension type', 'booktics'),
                400,
                'invalid_extension_type'
            );
        }

        foreach ($types[$type] as $data) {
            if ( isset( $data['slug'] ) && 'woocommerce' === $data['slug'] ) {
                $data = $this->enrich_woocommerce( $data );
            }

            $modules_list[] = $data;
        }

        return $this->response($modules_list);
    }

    /**
     * Attach WooCommerce install state to the module card payload.
     *
     * The Modules screen does not load the settings REST payload, so the card
     * has no other way to know whether WooCommerce is installed. With this it
     * can offer the same Install/Activate action the settings panel does.
     *
     * @param array $data WooCommerce module row.
     * @return array
     */
    protected function enrich_woocommerce( array $data ): array {
        if ( ! function_exists( 'get_plugins' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $active    = class_exists( 'WooCommerce' );
        $installed = $active || array_key_exists( 'woocommerce/woocommerce.php', get_plugins() );

        $data['woocommerce_install_status'] = $active ? 1 : 0;

        // Installed but switched off gets a real one-click activation; not
        // installed at all is sent to WordPress' own plugin install screen.
        // Built with add_query_arg, not wp_nonce_url: the latter esc_html's its
        // output (& -> &#038;), which survives an HTML href but breaks once the
        // URL is carried through JSON and assigned to window.location.
        $data['woocommerce_activate_url'] = ( $installed && ! $active )
            ? add_query_arg(
                array(
                    'action'   => 'activate',
                    'plugin'   => 'woocommerce/woocommerce.php',
                    '_wpnonce' => wp_create_nonce( 'activate-plugin_woocommerce/woocommerce.php' ),
                ),
                admin_url( 'plugins.php' )
            )
            : '';
        $data['woocommerce_install_url'] = admin_url( 'plugin-install.php?tab=plugin-information&plugin=woocommerce&TB_iframe=true' );

        return $data;
    }

    /**
     * Update extension status
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_HTTP_Response Response object on success, or WP_Error object on failure.
     */
    public function update_item($request) {
        $params = json_decode( $request->get_body(), true);

        $name   = isset($params['name']) ? sanitize_text_field($params['name']) : '';
        $status = isset($params['status']) ? sanitize_text_field($params['status']) : '';

        $valid_statuses = ['off', 'on', 'install', 'activate', 'deactivate', 'upgrade'];

        if (empty($name)) {
            return $this->error(
                __('Please enter extension name', 'booktics'),
                422,
                'extension_name_error'
            );
        }

        if (empty($status)) {
            return $this->error(
                /* translators: %s: extension name */
                sprintf(__('Please provide status for extension %s', 'booktics'), $name),
                422,
                'extension_status_error'
            );
        }

        if (!in_array($status, $valid_statuses, true)) {
            return $this->error(
                /* translators: %s: extension status */
                sprintf(__('Invalid status %s provided', 'booktics'), $status),
                422,
                'invalid_status'
            );
        }

        // WooCommerce is a module in this list, but Install/Activate act on the
        // WooCommerce *plugin*, not the module toggle. Handle it up front so the
        // Modules card can bring WooCommerce up in place, without bouncing the
        // admin to wp-admin/plugins.php.
        if ( 'woocommerce' === $name && in_array( $status, array( 'install', 'activate' ), true ) ) {
            if ( 'install' === $status && ! $this->plugin_manager::is_installed( 'woocommerce' ) ) {
                $installed = $this->plugin_manager->install_plugin( 'woocommerce' );

                if ( is_wp_error( $installed ) ) {
                    return $this->error( $installed->get_error_message(), 500, 'operation_failed' );
                }

                if ( false === $installed ) {
                    return $this->error( __( 'Could not install WooCommerce.', 'booktics' ), 500, 'operation_failed' );
                }
            }

            $activated = $this->plugin_manager->activate_plugin( 'woocommerce' );

            if ( is_wp_error( $activated ) ) {
                return $this->error( $activated->get_error_message(), 500, 'operation_failed' );
            }

            if ( false === $activated ) {
                return $this->error( __( 'Could not activate WooCommerce.', 'booktics' ), 500, 'operation_failed' );
            }

            return $this->response(
                array( 'name' => $name, 'status' => 'activate' ),
                __( 'WooCommerce activated successfully', 'booktics' )
            );
        }

        $extension = $this->extensions->find($name);

        if (!$extension) {
            return $this->error(
                /* translators: %s: extension name */
                sprintf(__('Extension %s not found', 'booktics'), $name),
                404,
                'extension_not_found'
            );
        }

        if ( in_array( $extension['type'], array( 'module', 'integration' ), true ) ) {
            $result = $this->extensions->update($name, $status);

            if (false === $result) {
                return $this->error(
                    /* translators: %s: extension name */
                    sprintf(__('Could not turn %s the module', 'booktics'), $status),
                    500,
                    'operation_failed'
                );
            }

            $this->update_relevant_settings($name, $status);

            /**
             * Fires after a module has been turned on or off.
             *
             * Lets a module react to being enabled without polling, e.g. to
             * reset a throttle so its first sync runs immediately.
             *
             * @param string $name   Module slug.
             * @param string $status New status.
             */
            do_action( 'booktics_module_status_updated', $name, $status );

            return $this->response( [
                'name'   => $name, 
                'status' => $status 
            ],
            /* translators: %s: extension name */
            sprintf( __('Module turned %s successfully', 'booktics'), $status) );
        }

        // When updating plugin status using tools-sdk change actual plugin status
        if ( 'plugin' === $extension['type'] ) {
            switch ( $status ) {
                case 'install':
                    $result = $this->plugin_manager->install_plugin($name);
                    break;
                case 'activate':
                    $result = $this->plugin_manager->activate_plugin($name);
                    break;
                case 'deactivate':
                    $result = $this->plugin_manager->deactivate_plugin($name);
                    break;
                case 'upgrade':
                    return $this->response( [ 'redirect_url' => $extension['upgrade_link'] ] );
                    break;
            }

            if ( false === $result ) {
                return $this->error(
                    /* translators: %s: extension name */
                    sprintf(__('Could not %s the extension', 'booktics'), $status),
                    500,
                    'operation_failed'
                );
            }
        }

        return $this->response([
            'name'   => $name,
            'status' => $status,
        ],
        /* translators: %s: extension name */
        sprintf(__('Extension %s successfully', 'booktics'), $status)
        );
    }

    /**
     * Update relevant settings based on extension status
     *
     * @since 1.0.0
     *
     * @param string $extension_name   Extension name.
     * @param string $extension_status Extension status.
     * @return void
     */
    public function update_relevant_settings($extension_name, $extension_status) {
        $is_enabled = $extension_status === 'on';

        switch ($extension_name) {
            case 'stripe':
                booktics_update_option('stripe_addon_install_status', $is_enabled ? 1 : 0);
                booktics_update_option('enable_stripe', $is_enabled);
                break;

            case 'paypal':
                booktics_update_option('paypal_connected', $is_enabled);
                booktics_update_option('enable_paypal', $is_enabled);
                break;

            case 'woocommerce':
                // The booking frontend gates the gateway on this setting, so a
                // module toggled from the Modules screen must flip it too;
                // otherwise the module boots but no customer can select it.
                booktics_update_option('enable_woocommerce_payment', $is_enabled);
                break;

            case 'google-calendar':
                booktics_update_option('google_redirect_url', booktics_get_auth_redirect_uri('google-auth'));
                booktics_update_option('google_calendar_connected', false);
                booktics_update_option('enable_google_calendar', $is_enabled);
                break;

            case 'outlook-calendar':
                booktics_update_option('microsoft_redirect_url', booktics_get_auth_redirect_uri('microsoft-auth'));
                booktics_update_option('enable_outlook_calendar', $is_enabled);
                break;

            case 'fluentcrm':
                booktics_update_option('enable_fluentcrm', $is_enabled);
                break;

            case 'pabbly':
                booktics_update_option('enable_pabbly', $is_enabled);
                break;

            case 'zapier':
                booktics_update_option('enable_zapier', $is_enabled);
                break;

            case 'zoho':
                booktics_update_option('enable_zoho', $is_enabled);
                break;

            case 'mailpoet':
                booktics_update_option('enable_mailpoet', $is_enabled);
                break;
        }
    }
}



```
