modules-page.php
369 lines
| 1 | <?php |
| 2 | if ( ! defined( 'ABSPATH' ) ) { |
| 3 | exit; |
| 4 | } |
| 5 | |
| 6 | if ( ! function_exists( 'is_plugin_active' ) ) { |
| 7 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 8 | } |
| 9 | |
| 10 | // ── Integrations data ────────────────────────────────────────────────────────── |
| 11 | $integrations = [ |
| 12 | [ |
| 13 | 'id' => 'aicom', |
| 14 | 'title' => 'AICOM — AI Commander', |
| 15 | 'description' => 'Use your AI subscription to manage WordPress: create Elementor pages, update content, automate tasks, and stay fully in control.', |
| 16 | 'plugin_file' => 'aicom/aicom.php', |
| 17 | 'link' => admin_url( 'plugin-install.php?s=aicom&tab=search&type=term' ), |
| 18 | 'link_target' => '_self', |
| 19 | 'bg' => '#efffdb', |
| 20 | ], |
| 21 | [ |
| 22 | 'id' => 'widget_finder', |
| 23 | 'title' => 'Widget Finder for Elementor', |
| 24 | 'description' => 'Search and install Elementor widgets without leaving the editor. Discover thousands of widgets from WordPress.org in a single click.', |
| 25 | 'plugin_file' => 'widget-finder-for-elementor/widget-finder-for-elementor.php', |
| 26 | 'link' => 'https://github.com/dudaster/widget-finder-for-elementor/releases/latest', |
| 27 | 'link_target' => '_blank', |
| 28 | 'bg' => '#fff3e0', |
| 29 | ], |
| 30 | [ |
| 31 | 'id' => 'ele_conditions', |
| 32 | 'title' => 'Ele Conditions', |
| 33 | 'description' => 'Trigger actions on any Elementor element: show, hide, animate, or redirect on click, scroll, hover, or exit — no code needed.', |
| 34 | 'plugin_file' => 'ele-conditions/ele-conditions.php', |
| 35 | 'link' => admin_url( 'plugin-install.php?s=ele+conditions&tab=search&type=term' ), |
| 36 | 'link_target' => '_self', |
| 37 | 'bg' => '#e0f8f8', |
| 38 | ], |
| 39 | ]; |
| 40 | |
| 41 | $manager = ECS_Core::instance()->modules(); |
| 42 | $pro_active = defined( 'ELECSP_VER' ); |
| 43 | $pro_installed = $pro_active || file_exists( WP_PLUGIN_DIR . '/ele-custom-skin-pro/ele-custom-skin-pro.php' ); |
| 44 | $pro_activate_url = admin_url( 'plugins.php?action=activate&plugin=' . urlencode( 'ele-custom-skin-pro/ele-custom-skin-pro.php' ) . '&_wpnonce=' . wp_create_nonce( 'activate-plugin_ele-custom-skin-pro/ele-custom-skin-pro.php' ) ); |
| 45 | |
| 46 | // Static list of Pro modules — shown as locked placeholders when Pro is not installed. |
| 47 | $pro_stubs = [ |
| 48 | [ |
| 49 | 'id' => 'dynamic_repeater', |
| 50 | 'title' => __( 'Dynamic Repeater Builder', 'ele-custom-skin' ), |
| 51 | 'description' => __( 'Populate any Elementor repeater from Posts, Terms, ACF, or JSON. Supports one-time generation and runtime data binding.', 'ele-custom-skin' ), |
| 52 | ], |
| 53 | [ |
| 54 | 'id' => 'container_responsive', |
| 55 | 'title' => __( 'Responsive Container Layout', 'ele-custom-skin' ), |
| 56 | 'description' => __( 'Set a different container type (Flexbox, Grid, Slider, Custom Layout) for each breakpoint — desktop, tablet, and mobile independently.', 'ele-custom-skin' ), |
| 57 | ], |
| 58 | [ |
| 59 | 'id' => 'color_schemes', |
| 60 | 'title' => __( 'Color Schemes', 'ele-custom-skin' ), |
| 61 | 'description' => __( 'Save named colour palettes and switch them globally or per page — same concept as Global Colors but switchable on the fly.', 'ele-custom-skin' ), |
| 62 | ], |
| 63 | [ |
| 64 | 'id' => 'font_schemes', |
| 65 | 'title' => __( 'Font Schemes', 'ele-custom-skin' ), |
| 66 | 'description' => __( 'Save named typography sets and switch them globally or per page — the same concept as Color Schemes but for fonts.', 'ele-custom-skin' ), |
| 67 | ], |
| 68 | [ |
| 69 | 'id' => 'alt_logos', |
| 70 | 'title' => __( 'Alternative Site Logos', 'ele-custom-skin' ), |
| 71 | 'description' => __( 'Define multiple logos and serve a different one based on page conditions or time of day — useful for seasonal branding or per-section identity.', 'ele-custom-skin' ), |
| 72 | ], |
| 73 | [ |
| 74 | 'id' => 'custom_look_feel', |
| 75 | 'title' => __( 'Custom Look & Feel', 'ele-custom-skin' ), |
| 76 | 'description' => __( 'Build conditional "looks" that activate a colour scheme, font scheme, CSS snippets, and a logo based on page conditions and time intervals.', 'ele-custom-skin' ), |
| 77 | ], |
| 78 | [ |
| 79 | 'id' => 'legacy_pro', |
| 80 | 'title' => __( 'Legacy Pro', 'ele-custom-skin' ), |
| 81 | 'description' => __( 'Original ECS Pro functionality kept for backward compatibility.', 'ele-custom-skin' ), |
| 82 | ], |
| 83 | ]; |
| 84 | |
| 85 | // Split registered modules into free / pro. |
| 86 | $free_modules = []; |
| 87 | $pro_modules = []; // only populated when Pro is installed |
| 88 | |
| 89 | foreach ( $manager->get_all() as $module ) { |
| 90 | if ( $module->is_pro() ) { |
| 91 | $pro_modules[] = $module; |
| 92 | } else { |
| 93 | $free_modules[] = $module; |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | // Legacy/deprecated modules go last in both lists. |
| 98 | $push_deprecated_last = fn( $a, $b ) => $a->is_deprecated() <=> $b->is_deprecated(); |
| 99 | usort( $free_modules, $push_deprecated_last ); |
| 100 | usort( $pro_modules, $push_deprecated_last ); |
| 101 | |
| 102 | // For card rendering: "locked" means pro not active (installed but inactive, or not installed at all). |
| 103 | $pro_locked_global = ! $pro_active; |
| 104 | |
| 105 | /** |
| 106 | * Render a single module card from a real module object. |
| 107 | */ |
| 108 | $render_card = function ( $module ) use ( $manager, $pro_active ) { |
| 109 | $is_active = $manager->is_active( $module->get_id() ); |
| 110 | $is_pro = $module->is_pro(); |
| 111 | $is_deprecated = $module->is_deprecated(); |
| 112 | $pro_locked = $is_pro && ! $pro_active; |
| 113 | |
| 114 | $preview_file = ECS_PATH . 'assets/module-previews/' . $module->get_id() . '.gif'; |
| 115 | $preview_url = file_exists( $preview_file ) |
| 116 | ? ECS_URL . 'assets/module-previews/' . $module->get_id() . '.gif' |
| 117 | : ''; |
| 118 | |
| 119 | $card_classes = implode( ' ', array_filter( [ |
| 120 | 'ecs-module-card', |
| 121 | $is_active ? 'is-active' : '', |
| 122 | $is_pro ? 'is-pro' : '', |
| 123 | $pro_locked ? 'is-locked' : '', |
| 124 | $is_deprecated ? 'is-deprecated' : '', |
| 125 | ] ) ); |
| 126 | ?> |
| 127 | <div class="<?php echo esc_attr( $card_classes ); ?>"> |
| 128 | |
| 129 | <div class="ecs-module-card__header"> |
| 130 | <span class="ecs-module-card__title"><?php echo esc_html( $module->get_title() ); ?></span> |
| 131 | <div class="ecs-module-card__badges"> |
| 132 | <?php if ( $is_deprecated ) : ?> |
| 133 | <span class="ecs-badge ecs-badge--deprecated"><?php esc_html_e( 'Legacy', 'ele-custom-skin' ); ?></span> |
| 134 | <?php elseif ( $is_pro ) : ?> |
| 135 | <span class="ecs-badge ecs-badge--pro"><?php esc_html_e( 'PRO', 'ele-custom-skin' ); ?></span> |
| 136 | <?php else : ?> |
| 137 | <span class="ecs-badge ecs-badge--free"><?php esc_html_e( 'FREE', 'ele-custom-skin' ); ?></span> |
| 138 | <?php endif; ?> |
| 139 | |
| 140 | <?php if ( $preview_url ) : ?> |
| 141 | <div class="ecs-preview-wrap"> |
| 142 | <button type="button" |
| 143 | class="ecs-preview-btn <?php echo $pro_locked ? 'ecs-preview-btn--locked' : ''; ?>" |
| 144 | title="<?php esc_attr_e( 'See preview', 'ele-custom-skin' ); ?>"> |
| 145 | <span class="dashicons dashicons-video-alt3"></span> |
| 146 | </button> |
| 147 | <div class="ecs-preview-popup"> |
| 148 | <img src="<?php echo esc_url( $preview_url ); ?>" |
| 149 | alt="<?php echo esc_attr( $module->get_title() ); ?> preview" |
| 150 | loading="lazy"> |
| 151 | </div> |
| 152 | </div> |
| 153 | <?php endif; ?> |
| 154 | </div> |
| 155 | </div> |
| 156 | |
| 157 | <?php if ( $module->get_description() ) : ?> |
| 158 | <p class="ecs-module-card__desc"><?php echo esc_html( $module->get_description() ); ?></p> |
| 159 | <?php endif; ?> |
| 160 | |
| 161 | <?php $deprecated_notice = $is_deprecated ? $module->get_deprecated_notice() : ''; ?> |
| 162 | <?php if ( $deprecated_notice ) : ?> |
| 163 | <p class="ecs-module-deprecated-notice"> |
| 164 | <?php echo esc_html( $deprecated_notice ); ?> |
| 165 | </p> |
| 166 | <?php endif; ?> |
| 167 | |
| 168 | <div class="ecs-module-card__footer"> |
| 169 | <?php if ( $pro_locked ) : ?> |
| 170 | <span class="ecs-module-locked"><?php esc_html_e( 'Requires ECS Pro', 'ele-custom-skin' ); ?></span> |
| 171 | <?php else : ?> |
| 172 | <label class="ecs-toggle"> |
| 173 | <input type="checkbox" |
| 174 | name="ecs_modules[]" |
| 175 | value="<?php echo esc_attr( $module->get_id() ); ?>" |
| 176 | <?php checked( $is_active ); ?> |
| 177 | > |
| 178 | <span class="ecs-toggle__slider"></span> |
| 179 | <span class="ecs-toggle__label ecs-toggle__label--active"><?php esc_html_e( 'Active', 'ele-custom-skin' ); ?></span> |
| 180 | <span class="ecs-toggle__label ecs-toggle__label--inactive"><?php esc_html_e( 'Inactive', 'ele-custom-skin' ); ?></span> |
| 181 | </label> |
| 182 | <?php endif; ?> |
| 183 | </div> |
| 184 | |
| 185 | </div> |
| 186 | <?php |
| 187 | }; |
| 188 | |
| 189 | /** |
| 190 | * Render a locked Pro stub card (Pro not installed). |
| 191 | */ |
| 192 | $render_stub = function ( array $stub ) { |
| 193 | $preview_file = ECS_PATH . 'assets/module-previews/' . $stub['id'] . '.gif'; |
| 194 | $preview_url = file_exists( $preview_file ) |
| 195 | ? ECS_URL . 'assets/module-previews/' . $stub['id'] . '.gif' |
| 196 | : ''; |
| 197 | |
| 198 | $module_id = $stub['id']; |
| 199 | $module_title = $stub['title']; |
| 200 | $module_desc = $stub['description']; |
| 201 | $is_active = false; |
| 202 | $is_deprecated = false; |
| 203 | $pro_locked = true; |
| 204 | ?> |
| 205 | <div class="ecs-module-card is-pro is-locked"> |
| 206 | |
| 207 | <div class="ecs-module-card__header"> |
| 208 | <span class="ecs-module-card__title"><?php echo esc_html( $module_title ); ?></span> |
| 209 | <div class="ecs-module-card__badges"> |
| 210 | <span class="ecs-badge ecs-badge--pro"><?php esc_html_e( 'PRO', 'ele-custom-skin' ); ?></span> |
| 211 | <?php if ( $preview_url ) : ?> |
| 212 | <div class="ecs-preview-wrap"> |
| 213 | <button type="button" class="ecs-preview-btn ecs-preview-btn--locked" |
| 214 | title="<?php esc_attr_e( 'See preview', 'ele-custom-skin' ); ?>"> |
| 215 | <span class="dashicons dashicons-video-alt3"></span> |
| 216 | </button> |
| 217 | <div class="ecs-preview-popup"> |
| 218 | <img src="<?php echo esc_url( $preview_url ); ?>" |
| 219 | alt="<?php echo esc_attr( $module_title ); ?> preview" |
| 220 | loading="lazy"> |
| 221 | </div> |
| 222 | </div> |
| 223 | <?php endif; ?> |
| 224 | </div> |
| 225 | </div> |
| 226 | |
| 227 | <?php if ( $module_desc ) : ?> |
| 228 | <p class="ecs-module-card__desc"><?php echo esc_html( $module_desc ); ?></p> |
| 229 | <?php endif; ?> |
| 230 | |
| 231 | <div class="ecs-module-card__footer"> |
| 232 | <span class="ecs-module-locked"><?php esc_html_e( 'Requires ECS Pro', 'ele-custom-skin' ); ?></span> |
| 233 | </div> |
| 234 | |
| 235 | </div> |
| 236 | <?php |
| 237 | }; |
| 238 | /** |
| 239 | * Render a companion plugin integration card. |
| 240 | */ |
| 241 | $render_integration = function( array $int ) { |
| 242 | $installed = file_exists( WP_PLUGIN_DIR . '/' . $int['plugin_file'] ); |
| 243 | $active = $installed && is_plugin_active( $int['plugin_file'] ); |
| 244 | $inactive = $installed && ! $active; |
| 245 | |
| 246 | $card_classes = 'ecs-module-card ecs-integration-card' . ( $active ? ' is-active' : '' ); |
| 247 | ?> |
| 248 | <div class="<?php echo esc_attr( $card_classes ); ?>" style="--ecs-int-bg:<?php echo esc_attr( $int['bg'] ); ?>"> |
| 249 | |
| 250 | <div class="ecs-module-card__header"> |
| 251 | <span class="ecs-module-card__title"><?php echo esc_html( $int['title'] ); ?></span> |
| 252 | <div class="ecs-module-card__badges"> |
| 253 | <span class="ecs-badge ecs-badge--free"><?php esc_html_e( 'FREE', 'ele-custom-skin' ); ?></span> |
| 254 | </div> |
| 255 | </div> |
| 256 | |
| 257 | <p class="ecs-module-card__desc"><?php echo esc_html( $int['description'] ); ?></p> |
| 258 | |
| 259 | <div class="ecs-module-card__footer"> |
| 260 | <?php if ( $active ) : ?> |
| 261 | <span class="ecs-toggle ecs-int-toggle ecs-int-toggle--active"> |
| 262 | <span class="ecs-toggle__slider"></span> |
| 263 | <span class="ecs-toggle__label ecs-toggle__label--active"><?php esc_html_e( 'Active', 'ele-custom-skin' ); ?></span> |
| 264 | </span> |
| 265 | <?php elseif ( $inactive ) : ?> |
| 266 | <a href="<?php echo esc_url( admin_url( 'plugins.php' ) ); ?>" |
| 267 | class="ecs-toggle ecs-int-toggle ecs-int-toggle--not-installed"> |
| 268 | <span class="ecs-toggle__slider"></span> |
| 269 | <span class="ecs-int-status"><?php esc_html_e( 'Inactive', 'ele-custom-skin' ); ?></span> |
| 270 | </a> |
| 271 | <?php else : ?> |
| 272 | <a href="<?php echo esc_url( $int['link'] ); ?>" |
| 273 | class="ecs-toggle ecs-int-toggle ecs-int-toggle--not-installed" |
| 274 | <?php if ( '_blank' === $int['link_target'] ) : ?>target="_blank" rel="noopener noreferrer"<?php endif; ?>> |
| 275 | <span class="ecs-toggle__slider"></span> |
| 276 | <span class="ecs-int-status"><?php esc_html_e( 'Not installed', 'ele-custom-skin' ); ?></span> |
| 277 | </a> |
| 278 | <?php endif; ?> |
| 279 | </div> |
| 280 | |
| 281 | </div> |
| 282 | <?php |
| 283 | }; |
| 284 | ?> |
| 285 | <div class="wrap ecs-admin-wrap"> |
| 286 | |
| 287 | <div class="ecs-admin-header"> |
| 288 | <h1><?php esc_html_e( 'ECS — Ele Custom Skin for Elementor', 'ele-custom-skin' ); ?></h1> |
| 289 | <span class="ecs-version">v<?php echo esc_html( ECS_VERSION ); ?></span> |
| 290 | </div> |
| 291 | |
| 292 | <?php if ( isset( $_GET['updated'] ) ) : ?> |
| 293 | <div class="notice notice-success is-dismissible"> |
| 294 | <p><?php esc_html_e( 'Module settings saved.', 'ele-custom-skin' ); ?></p> |
| 295 | </div> |
| 296 | <?php endif; ?> |
| 297 | |
| 298 | <!-- ── INTEGRATIONS ──────────────────────────────────────── --> |
| 299 | <div class="ecs-section-header ecs-section-header--integrations"> |
| 300 | <h2 class="ecs-section-title"><?php esc_html_e( 'Integrations', 'ele-custom-skin' ); ?></h2> |
| 301 | </div> |
| 302 | <div class="ecs-modules-grid ecs-integrations-grid"> |
| 303 | <?php foreach ( $integrations as $int ) : ?> |
| 304 | <?php $render_integration( $int ); ?> |
| 305 | <?php endforeach; ?> |
| 306 | </div> |
| 307 | |
| 308 | <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> |
| 309 | <?php wp_nonce_field( 'ecs_save_modules' ); ?> |
| 310 | <input type="hidden" name="action" value="ecs_save_modules"> |
| 311 | |
| 312 | <!-- ── FREE modules ───────────────────────────────────── --> |
| 313 | <div class="ecs-section-header"> |
| 314 | <h2 class="ecs-section-title"><?php esc_html_e( 'Free Modules', 'ele-custom-skin' ); ?></h2> |
| 315 | </div> |
| 316 | <div class="ecs-modules-grid"> |
| 317 | <?php foreach ( $free_modules as $module ) : ?> |
| 318 | <?php $render_card( $module ); ?> |
| 319 | <?php endforeach; ?> |
| 320 | </div> |
| 321 | |
| 322 | <!-- ── PRO modules ────────────────────────────────────── --> |
| 323 | <div class="ecs-section-header ecs-section-header--pro"> |
| 324 | <h2 class="ecs-section-title"> |
| 325 | <?php esc_html_e( 'Pro Modules', 'ele-custom-skin' ); ?> |
| 326 | <?php if ( $pro_active ) : ?> |
| 327 | <span class="ecs-pro-active-badge"><?php esc_html_e( 'Active', 'ele-custom-skin' ); ?></span> |
| 328 | <?php |
| 329 | $license_key = get_option( 'elecs-license-key', '' ); |
| 330 | if ( ! empty( $license_key ) ) : |
| 331 | ?> |
| 332 | <a href="<?php echo esc_url( admin_url( 'admin.php?page=ecs-license' ) ); ?>" class="ecs-license-badge"> |
| 333 | <span class="dashicons dashicons-yes-alt"></span> |
| 334 | <?php esc_html_e( 'Licensed', 'ele-custom-skin' ); ?> |
| 335 | </a> |
| 336 | <?php else : ?> |
| 337 | <a href="<?php echo esc_url( admin_url( 'admin.php?page=ecs-license' ) ); ?>" class="ecs-license-badge ecs-license-badge--activate"> |
| 338 | <span class="dashicons dashicons-admin-network"></span> |
| 339 | <?php esc_html_e( 'Activate License', 'ele-custom-skin' ); ?> |
| 340 | </a> |
| 341 | <?php endif; ?> |
| 342 | <?php elseif ( $pro_installed ) : ?> |
| 343 | <a href="<?php echo esc_url( $pro_activate_url ); ?>" class="ecs-pro-cta ecs-pro-cta--activate"> |
| 344 | <?php esc_html_e( 'Activate ECS Pro →', 'ele-custom-skin' ); ?> |
| 345 | </a> |
| 346 | <?php else : ?> |
| 347 | <a href="https://dudaster.com/ecs-pro/" target="_blank" class="ecs-pro-cta"> |
| 348 | <?php esc_html_e( 'Get ECS Pro →', 'ele-custom-skin' ); ?> |
| 349 | </a> |
| 350 | <?php endif; ?> |
| 351 | </h2> |
| 352 | </div> |
| 353 | <div class="ecs-modules-grid"> |
| 354 | <?php if ( $pro_active ) : ?> |
| 355 | <?php foreach ( $pro_modules as $module ) : ?> |
| 356 | <?php $render_card( $module ); ?> |
| 357 | <?php endforeach; ?> |
| 358 | <?php else : ?> |
| 359 | <?php foreach ( $pro_stubs as $stub ) : ?> |
| 360 | <?php $render_stub( $stub ); ?> |
| 361 | <?php endforeach; ?> |
| 362 | <?php endif; ?> |
| 363 | </div> |
| 364 | |
| 365 | <?php submit_button( __( 'Save Settings', 'ele-custom-skin' ), 'primary ecs-save-btn' ); ?> |
| 366 | |
| 367 | </form> |
| 368 | </div> |
| 369 |