| @@ -3,34 +3,32 @@ | ||
| 3 | 3 | /** |
| 4 | 4 | * Plugin Loader. |
| 5 | 5 | * |
| 6 | 6 | * @package IvyForms |
| 7 | - * @since 0.0.1 | |
| 7 | + * @since 0.1.0 | |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | 10 | namespace IvyForms\Plugin; |
| 11 | 11 | |
| 12 | -// phpcs:disable PSR1.Files.SideEffects | |
| 13 | -if (!defined('ABSPATH')) { | |
| 14 | - exit; // Exit if accessed directly | |
| 15 | -} | |
| 16 | - | |
| 17 | -use IvyForms\Vendor\DI\Container; | |
| 18 | -use IvyForms\Vendor\DI\DependencyException; | |
| 19 | -use IvyForms\Vendor\DI\NotFoundException; | |
| 20 | 12 | use Exception; |
| 21 | 13 | use IvyForms\Common\Exceptions\InvalidArgumentException; |
| 14 | +use IvyForms\Common\Exceptions\ValidationException; | |
| 22 | 15 | use IvyForms\Routes\Routes; |
| 23 | 16 | use IvyForms\Services\InstallActions\ActivationDatabaseHook; |
| 24 | -use IvyForms\Services\InstallActions\ActivationMultisite; | |
| 25 | -use IvyForms\Services\InstallActions\ActivationNewSiteMultisite; | |
| 26 | -use IvyForms\Services\InstallActions\ActivationSettingsHook; | |
| 27 | -use IvyForms\Services\InstallActions\DeleteDatabaseHook; | |
| 28 | -use IvyForms\Services\InstallActions\DeletionMultisite; | |
| 17 | +use IvyForms\Services\Integrations\Elementor\IvyFormsElementorLoader; | |
| 18 | +use IvyForms\Services\Integrations\Gutenberg\Blocks\GutenbergBlock; | |
| 19 | +use IvyForms\Services\Integrations\IntegrationService; | |
| 29 | 20 | use IvyForms\Services\Menu\MenuManager; |
| 21 | +use IvyForms\Services\Payment\PaymentCspHeaders; | |
| 30 | 22 | use IvyForms\Services\Preview\PreviewService; |
| 31 | 23 | use IvyForms\Services\Settings\SettingsService; |
| 24 | +use IvyForms\Services\Shortcode\Helpers\ShortcodeHelper; | |
| 32 | 25 | use IvyForms\Services\Shortcode\ShortcodeService; |
| 26 | +use IvyForms\Services\Usage\UsageTrackingService; | |
| 27 | +use IvyForms\Vendor\DI\Container; | |
| 28 | +use IvyForms\Vendor\DI\ContainerBuilder; | |
| 29 | +use IvyForms\Vendor\DI\DependencyException; | |
| 30 | +use IvyForms\Vendor\DI\NotFoundException; | |
| 33 | 31 | |
| 34 | 32 | /** |
| 35 | 33 | * Plugin |
| 36 | 34 | */ |
| @@ -40,8 +38,9 @@ | ||
| 40 | 38 | * Instance |
| 41 | 39 | */ |
| 42 | 40 | private static ?object $instance = null; |
| 43 | 41 | |
| 42 | + private ContainerBuilder $builder; | |
| 44 | 43 | /** |
| 45 | 44 | * Container |
| 46 | 45 | */ |
| 47 | 46 | public ?Container $container = null; |
| @@ -56,12 +55,12 @@ | ||
| 56 | 55 | * |
| 57 | 56 | * @return object initialized object of class. |
| 58 | 57 | * @throws Exception |
| 59 | 58 | */ |
| 60 | - public static function getInstance(Container $container): ?object | |
| 59 | + public static function getInstance(): ?object | |
| 61 | 60 | { |
| 62 | 61 | if (null === self::$instance) { |
| 63 | - self::$instance = new self($container); | |
| 62 | + self::$instance = new self(); | |
| 64 | 63 | } |
| 65 | 64 | return self::$instance; |
| 66 | 65 | } |
| 67 | 66 | |
| @@ -68,13 +67,27 @@ | ||
| 68 | 67 | /** |
| 69 | 68 | * Constructor |
| 70 | 69 | * |
| 71 | 70 | * @throws Exception |
| 72 | - * @since 0.0.1 | |
| 71 | + * @since 0.1.0 | |
| 73 | 72 | */ |
| 74 | - public function __construct(Container $container) | |
| 73 | + public function __construct() | |
| 75 | 74 | { |
| 76 | - $this->container = $container; | |
| 75 | + $this->builder = new ContainerBuilder(); | |
| 76 | + $this->builder->addDefinitions(IVYFORMS_PATH . '/backend/src/Config/repository.php'); | |
| 77 | + $this->builder->addDefinitions(IVYFORMS_PATH . '/backend/src/Config/services.php'); | |
| 78 | + | |
| 79 | + /** | |
| 80 | + * Allow to hook into container before build | |
| 81 | + * @since 0.1.0 | |
| 82 | + * | |
| 83 | + * Arguments: | |
| 84 | + * - ContainerBuilder $builder The IvyForms DI container builder (base) | |
| 85 | + */ | |
| 86 | + do_action('ivyforms/boot/extend_container_builder', $this->builder); | |
| 87 | + | |
| 88 | + $this->container = $this->builder->build(); | |
| 89 | + | |
| 77 | 90 | $this->settingsService = $this->container->get(SettingsService::class); |
| 78 | 91 | |
| 79 | 92 | /** |
| 80 | 93 | * The code that runs during plugin activation |
| @@ -80,26 +93,39 @@ | ||
| 80 | 93 | * The code that runs during plugin activation |
| 81 | 94 | */ |
| 82 | 95 | register_activation_hook( |
| 83 | 96 | IVYFORMS_FILE, |
| 84 | - [ self::class, 'activation' ] | |
| 97 | + [ PluginInstallLifecycle::class, 'activation' ] | |
| 85 | 98 | ); |
| 86 | 99 | register_activation_hook( |
| 87 | 100 | IVYFORMS_FILE, |
| 88 | - [ self::class, 'activationSettings' ] | |
| 101 | + [ PluginInstallLifecycle::class, 'activationSettings' ] | |
| 89 | 102 | ); |
| 90 | 103 | |
| 91 | 104 | /** |
| 92 | 105 | * The code that runs during plugin deactivation |
| 93 | 106 | */ |
| 107 | + register_deactivation_hook( | |
| 108 | + IVYFORMS_FILE, | |
| 109 | + [ PluginInstallLifecycle::class, 'deactivation' ] | |
| 110 | + ); | |
| 111 | + | |
| 112 | + /** | |
| 113 | + * The code that runs when plugin is deleted | |
| 114 | + */ | |
| 94 | 115 | register_uninstall_hook( |
| 95 | 116 | IVYFORMS_FILE, |
| 96 | - [ self::class, 'deletion'] | |
| 117 | + [ PluginInstallLifecycle::class, 'deletion'] | |
| 97 | 118 | ); |
| 98 | 119 | |
| 99 | 120 | /** Init load */ |
| 100 | 121 | add_action('plugins_loaded', [ $this, 'init' ]); |
| 101 | 122 | |
| 123 | + add_action('init', [PluginInstallLifecycle::class, 'maybeEnsureAdministratorCapabilities'], 0); | |
| 124 | + | |
| 125 | + /** Load translations at init action (WordPress 6.7+ requirement) */ | |
| 126 | + add_action('init', [ $this, 'loadTranslations' ]); | |
| 127 | + | |
| 102 | 128 | /** Register API calls */ |
| 103 | 129 | add_action('rest_api_init', [$this, 'registerRoutes']); |
| 104 | 130 | |
| 105 | 131 | /** Isolate API calls */ |
| @@ -106,54 +132,18 @@ | ||
| 106 | 132 | add_action('wp_ajax_ivyforms_api', [ $this, 'registerRoutes' ]); |
| 107 | 133 | add_action('wp_ajax_nopriv_ivyforms_api', [ $this, 'registerRoutes' ]); |
| 108 | 134 | |
| 109 | 135 | /** Activation hook for new site on multisite setup */ |
| 110 | - add_action('wpmu_new_blog', [ $this, 'initNewSite' ]); | |
| 136 | + add_action('wpmu_new_blog', [ PluginInstallLifecycle::class, 'initNewSite' ]); | |
| 111 | 137 | |
| 112 | 138 | add_filter('wp_script_attributes', ['IvyForms\Services\Shortcode\ShortcodeService', 'addScriptAttribute']); |
| 113 | 139 | |
| 114 | - add_action('admin_init', [$this, 'adminInit']); | |
| 115 | - add_filter('admin_body_class', [$this, 'addAdminBodyClass']); | |
| 140 | + // Register admin hooks via AdminHooks class | |
| 141 | + $adminHooks = new AdminHooks($this->settingsService); | |
| 142 | + $adminHooks->register(); | |
| 116 | 143 | } |
| 117 | 144 | |
| 118 | 145 | /** |
| 119 | - * Handle admin initialization tasks | |
| 120 | - * | |
| 121 | - * @return void | |
| 122 | - */ | |
| 123 | - public function adminInit(): void | |
| 124 | - { | |
| 125 | - $this->checkVersionAndHandleActivation(); | |
| 126 | - | |
| 127 | - $this->removeAdminNotices(); | |
| 128 | - } | |
| 129 | - | |
| 130 | - /** | |
| 131 | - * Check a plugin version and handle activation/deactivation if needed | |
| 132 | - * | |
| 133 | - * @return void | |
| 134 | - */ | |
| 135 | - private function checkVersionAndHandleActivation(): void | |
| 136 | - { | |
| 137 | - if (!function_exists('activate_plugin')) { | |
| 138 | - // @phpstan-ignore requireOnce.fileNotFound | |
| 139 | - require_once ABSPATH . 'wp-admin/includes/plugin.php'; | |
| 140 | - } | |
| 141 | - | |
| 142 | - $settingsService = new SettingsService(); | |
| 143 | - | |
| 144 | - $currentVersion = IVYFORMS_VERSION; | |
| 145 | - $savedVersion = $settingsService->getSetting('general', 'version'); | |
| 146 | - | |
| 147 | - if ($savedVersion !== $currentVersion) { | |
| 148 | - deactivate_plugins(IVYFORMS_PLUGIN_SLUG); // @phpstan-ignore argument.type | |
| 149 | - activate_plugin(IVYFORMS_PLUGIN_SLUG); // @phpstan-ignore argument.type | |
| 150 | - | |
| 151 | - $settingsService->setSetting('general', 'version', $currentVersion); | |
| 152 | - } | |
| 153 | - } | |
| 154 | - | |
| 155 | - /** | |
| 156 | 146 | * @throws DependencyException |
| 157 | 147 | * @throws NotFoundException |
| 158 | 148 | */ |
| 159 | 149 | public function registerRoutes(): void |
| @@ -162,137 +152,136 @@ | ||
| 162 | 152 | Routes::registerRoutes($this->container); |
| 163 | 153 | } |
| 164 | 154 | |
| 165 | 155 | /** |
| 166 | - * Remove admin notices on IvyForms admin pages | |
| 156 | + * Init | |
| 157 | + * @return void | |
| 158 | + * @throws ValidationException|InvalidArgumentException | |
| 167 | 159 | */ |
| 168 | - public function removeAdminNotices(): void | |
| 160 | + public function init(): void | |
| 169 | 161 | { |
| 170 | - // Check if we're on an IvyForms admin page | |
| 171 | - if ($this->isIvyFormsAdminPage()) { | |
| 172 | - // Remove all admin notices | |
| 173 | - remove_all_actions('admin_notices'); | |
| 174 | - remove_all_actions('all_admin_notices'); | |
| 175 | - // For plugins using user_admin_notices or network_admin_notices | |
| 176 | - remove_all_actions('user_admin_notices'); | |
| 177 | - remove_all_actions('network_admin_notices'); | |
| 178 | - wp_register_style( | |
| 179 | - 'ivyforms_admin_global', | |
| 180 | - IVYFORMS_URL . 'frontend/css/global-admin.css', | |
| 181 | - array(), | |
| 182 | - IVYFORMS_VERSION | |
| 183 | - ); | |
| 184 | - wp_enqueue_style('ivyforms_admin_global'); | |
| 185 | - } | |
| 162 | + new PreviewService(); | |
| 163 | + $this->initIntegrationService(); | |
| 164 | + $this->initMenuManager(); | |
| 165 | + $this->registerShortcode(); | |
| 166 | + PaymentCspHeaders::register(); | |
| 167 | + $this->registerGutenbergBlock(); | |
| 168 | + $this->registerElementorWidget(); | |
| 169 | + $this->initUsageTrackingService(); | |
| 186 | 170 | } |
| 187 | 171 | |
| 188 | 172 | /** |
| 189 | - * Check if current page is an IvyForms admin page | |
| 173 | + * Initialize integration service | |
| 190 | 174 | * |
| 191 | - * @SuppressWarnings(PHPMD) | |
| 192 | - * | |
| 193 | - * @return bool | |
| 175 | + * @return void | |
| 194 | 176 | */ |
| 195 | - private function isIvyFormsAdminPage(): bool | |
| 177 | + private function initIntegrationService(): void | |
| 196 | 178 | { |
| 197 | - if (!is_admin()) { | |
| 198 | - return false; | |
| 179 | + try { | |
| 180 | + /** @var IntegrationService $integrationService */ | |
| 181 | + $integrationService = $this->container->get(IntegrationService::class); | |
| 182 | + $integrationService->init(); | |
| 183 | + } catch (DependencyException | NotFoundException $e) { | |
| 184 | + if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 185 | + error_log('Integration service error: ' . $e->getMessage()); | |
| 186 | + } | |
| 199 | 187 | } |
| 200 | - // Adjust this based on your page's actual slug/identifier | |
| 201 | - return (isset($_GET['page']) && strpos($_GET['page'], 'ivyforms') !== false); | |
| 202 | 188 | } |
| 203 | 189 | |
| 204 | 190 | /** |
| 205 | - * @param bool $networkWide | |
| 191 | + * Initialize usage tracking service | |
| 192 | + * | |
| 193 | + * @return void | |
| 206 | 194 | */ |
| 207 | - public static function activation(bool $networkWide): void | |
| 195 | + private function initUsageTrackingService(): void | |
| 208 | 196 | { |
| 209 | - //Network activation | |
| 210 | - if ($networkWide && function_exists('is_multisite') && is_multisite()) { | |
| 211 | - ActivationMultisite::init(); | |
| 197 | + try { | |
| 198 | + /** @var UsageTrackingService $usageTrackingService */ | |
| 199 | + $usageTrackingService = $this->container->get(UsageTrackingService::class); | |
| 200 | + $usageTrackingService->init(); | |
| 201 | + } catch (DependencyException | NotFoundException $e) { | |
| 202 | + if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 203 | + error_log('Usage tracking service error: ' . $e->getMessage()); | |
| 204 | + } | |
| 212 | 205 | } |
| 213 | - | |
| 214 | - ActivationDatabaseHook::init(); | |
| 215 | 206 | } |
| 216 | 207 | |
| 217 | 208 | /** |
| 218 | - * @param int $siteId | |
| 209 | + * Initialize menu manager | |
| 210 | + * | |
| 211 | + * @return void | |
| 219 | 212 | */ |
| 220 | - public static function initNewSite(int $siteId): void | |
| 213 | + private function initMenuManager(): void | |
| 221 | 214 | { |
| 222 | - ActivationNewSiteMultisite::init($siteId); | |
| 215 | + try { | |
| 216 | + /** @var MenuManager $menuManager*/ | |
| 217 | + $menuManager = $this->container->get(MenuManager::class); | |
| 218 | + // Register the admin menus and submenus using the 'admin_menu' hook | |
| 219 | + $menuManager->registerMenus(); | |
| 220 | + } catch (DependencyException | NotFoundException $e) { | |
| 221 | + if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 222 | + error_log('Menu error: ' . $e->getMessage()); | |
| 223 | + } | |
| 224 | + } | |
| 223 | 225 | } |
| 224 | 226 | |
| 225 | 227 | /** |
| 226 | - * @throws Exception | |
| 228 | + * Register shortcode | |
| 229 | + * | |
| 230 | + * @return void | |
| 227 | 231 | */ |
| 228 | - public static function activationSettings(): void | |
| 232 | + private function registerShortcode(): void | |
| 229 | 233 | { |
| 230 | - ActivationSettingsHook::init(); | |
| 234 | + if (is_admin()) { | |
| 235 | + return; | |
| 236 | + } | |
| 237 | + | |
| 238 | + add_action('wp_enqueue_scripts', [ShortcodeHelper::class, 'registerFrontendStyleHandles'], 1); | |
| 239 | + | |
| 240 | + add_shortcode('ivyforms', function ($atts = []) { | |
| 241 | + return ShortcodeService::shortcodeHandler($atts); | |
| 242 | + }); | |
| 231 | 243 | } |
| 232 | 244 | |
| 233 | 245 | /** |
| 234 | - * Uninstall function when plugin is deleted | |
| 235 | - * @throws InvalidArgumentException | |
| 246 | + * Register Gutenberg block | |
| 247 | + * | |
| 248 | + * @return void | |
| 236 | 249 | */ |
| 237 | - public static function deletion(): void | |
| 250 | + private function registerGutenbergBlock(): void | |
| 238 | 251 | { |
| 239 | - $settingsService = new SettingsService(); | |
| 252 | + if (!function_exists('register_block_type')) { | |
| 253 | + return; | |
| 254 | + } | |
| 240 | 255 | |
| 241 | - if ($settingsService->getSetting('activation', 'deleteTables')) { | |
| 242 | - //Network deletion | |
| 243 | - if ( | |
| 244 | - function_exists('is_multisite') && | |
| 245 | - is_multisite() | |
| 246 | - ) { | |
| 247 | - DeletionMultisite::delete(); | |
| 248 | - } | |
| 249 | - | |
| 250 | - DeleteDatabaseHook::delete(); | |
| 251 | - } | |
| 256 | + add_action('init', [GutenbergBlock::class, 'register']); | |
| 252 | 257 | } |
| 253 | 258 | |
| 254 | 259 | /** |
| 255 | - * Init | |
| 260 | + * Register Elementor widget | |
| 261 | + * | |
| 256 | 262 | * @return void |
| 257 | 263 | */ |
| 258 | - public function init(): void | |
| 264 | + private function registerElementorWidget(): void | |
| 259 | 265 | { |
| 260 | - | |
| 261 | - new PreviewService(); | |
| 262 | - | |
| 263 | - try { | |
| 264 | - /** @var MenuManager $menuManager*/ | |
| 265 | - $menuManager = $this->container->get(MenuManager::class); | |
| 266 | - // Register the admin menus and submenus using the 'admin_menu' hook | |
| 267 | - $menuManager->registerMenus(); | |
| 268 | - } catch (DependencyException | NotFoundException $e) { | |
| 269 | - if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 270 | - error_log('Menu error: ' . $e->getMessage()); | |
| 271 | - } | |
| 266 | + if (!defined('ELEMENTOR_VERSION')) { | |
| 267 | + return; | |
| 272 | 268 | } |
| 273 | 269 | |
| 274 | - if (!is_admin()) { | |
| 275 | - add_shortcode('ivyforms', function ($atts = []) { | |
| 276 | - return ShortcodeService::shortcodeHandler($atts); | |
| 277 | - }); | |
| 278 | - } | |
| 270 | + IvyFormsElementorLoader::instance(); | |
| 279 | 271 | } |
| 272 | + | |
| 280 | 273 | /** |
| 281 | - * Add admin body class | |
| 274 | + * Load plugin translations | |
| 275 | + * Called on 'init' action to comply with WordPress 6.7+ requirements | |
| 282 | 276 | * |
| 283 | - * @param string $classes | |
| 284 | - * @return string | |
| 277 | + * @return void | |
| 285 | 278 | */ |
| 286 | - public function addAdminBodyClass(string $classes): string | |
| 279 | + public function loadTranslations(): void | |
| 287 | 280 | { |
| 288 | - if ($this->settingsService === null) { | |
| 289 | - return $classes; | |
| 290 | - } | |
| 291 | - | |
| 292 | - if ($this->settingsService->getSetting('general', 'fullscreen')) { | |
| 293 | - $classes .= ' ivyforms-fullscreen-mode '; | |
| 294 | - } | |
| 295 | - | |
| 296 | - return $classes; | |
| 281 | + load_plugin_textdomain( | |
| 282 | + 'ivyforms', | |
| 283 | + false, | |
| 284 | + dirname(plugin_basename(IVYFORMS_FILE)) . '/languages/' . get_locale() . '/' | |
| 285 | + ); | |
| 297 | 286 | } |
| 298 | 287 | } |