<?php

/**
 * Plugin Name:       PlugUpp Dynamic OG Image Generator
 * Plugin URI:        https://wordpress.org/plugins/dynamic-og-image-generator/
 * Description:       Automatically generates real screenshot-based Open Graph images for your pages and posts. No templates — actual screenshots of your site, cached locally and refreshed on a configurable schedule.
 * Version:           2.7.1
 * Requires at least: 6.0
 * Requires PHP:      7.4
 * Author:            PlugUpp
 * Author URI:        https://plugupp.com/
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       dynamic-og-image-generator
 * Domain Path:       /languages
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
// ----------------------------------------------------------------------------
// Free <-> Premium co-existence guard.
//
// During an upgrade from free to premium (or downgrade premium -> free),
// WordPress can briefly load BOTH plugin folders in the same request:
//
//     wp-content/plugins/dynamic-og-image-generator/dynamic-og-image-generator.php   (free)
//     wp-content/plugins/dynamic-og-image-generator-premium/dynamic-og-image-generator.php   (premium)
//
// Without protection, the second include redeclares functions / re-registers
// hooks and PHP fatals. The Freemius SDK's documented solution:
//
//   1. The FIRST file to load wins - it falls through to the `else` branch
//      and runs the full bootstrap, including defining doig_fs().
//   2. The SECOND file to load sees doig_fs() already exists, calls
//      doig_fs()->set_basename( true, __FILE__ ), and exits. set_basename
//      tells the SDK "the active install lives at THIS __FILE__", which
//      triggers the SDK to deactivate the other build automatically.
//
// Source: Freemius SDK Integration step 19.
// ----------------------------------------------------------------------------
if ( function_exists( 'doig_fs' ) ) {
    doig_fs()->set_basename( false, __FILE__ );
} else {
    // ------------------------------------------------------------------------
    // Freemius SDK bootstrap
    //
    // IMPORTANT — developer notes:
    //
    // 1. The public key below is safe to commit. Never put the secret key
    //    (sk_...) in plugin files. For local testing, add to wp-config.php:
    //
    //      define( 'WP_FS__DEV_MODE', true );
    //      define( 'WP_FS__SKIP_EMAIL_ACTIVATION', true );
    //      define( 'WP_FS__dynamic-og-image-generator_SECRET_KEY', 'sk_...' );
    //
    // 2. has_paid_plans is true — pricing plans must be configured in the
    //    Freemius dashboard before the checkout flow will work.
    //
    // 3. The free version distributed on WP.org uses is_premium => false.
    //    The premium zip served by Freemius uses is_premium => true.
    //    Use the Freemius deployment tool to build each variant.
    //
    // NOTE: the outer `if ( function_exists( 'doig_fs' ) )` already
    // prevents a second include from reaching this code. The inner
    // function_exists guard around the definition is kept anyway as
    // belt-and-braces - the Freemius SDK Integration page specifically
    // instructs to keep it. Removing it would break the outer guard's
    // own function_exists check on the first include of this file.
    // ------------------------------------------------------------------------
    if ( !function_exists( 'doig_fs' ) ) {
        function doig_fs() {
            global $doig_fs;
            if ( !isset( $doig_fs ) ) {
                require_once dirname( __FILE__ ) . '/vendor/freemius/start.php';
                $doig_fs = fs_dynamic_init( array(
                    'id'               => '29452',
                    'slug'             => 'dynamic-og-image-generator',
                    'type'             => 'plugin',
                    'public_key'       => 'pk_e746c006abd3928bc287eaebb4a75',
                    'is_premium'       => false,
                    'has_addons'       => false,
                    'has_paid_plans'   => true,
                    'is_org_compliant' => true,
                    'menu'             => array(
                        'slug'    => 'dynamic-og-image-generator',
                        'contact' => false,
                        'support' => false,
                    ),
                    'is_live'          => true,
                ) );
            }
            return $doig_fs;
        }

        // Initialise Freemius.
        doig_fs();
        // Tell Freemius where the plugin icon lives.
        //
        // Without this filter, the SDK's get_local_icon_url() derives
        // $img_dir from the SDK path (<plugin>/vendor/freemius/), so it
        // globs in <plugin>/vendor/freemius/assets/img/ instead of the
        // plugin's own assets/img/. The convention-based location at
        // <plugin>/assets/img/<slug>.png does not get picked up in that
        // configuration and the in-plugin Plans & Pricing page renders
        // with the default placeholder icon.
        //
        // The plugin_icon filter is the documented escape hatch:
        // https://freemius.com/help/documentation/wordpress-sdk/filters-actions-hooks/#plugin_icon
        // It overrides the path-derivation logic entirely - whatever
        // absolute path we return is used as-is.
        doig_fs()->add_filter( 'plugin_icon', function () {
            return __DIR__ . '/assets/img/dynamic-og-image-generator.png';
        } );
        // Signal that SDK was initialised — other code can hook here.
        do_action( 'doig_fs_loaded' );
    }
    // Plugin constants
    define( 'DOIG_VERSION', '2.7.1' );
    define( 'DOIG_PLUGIN_FILE', __FILE__ );
    define( 'DOIG_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    define( 'DOIG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    define( 'DOIG_PLUGIN_SLUG', 'dynamic-og-image-generator' );
    define( 'DOIG_OPTION_KEY', 'doig_settings' );
    // Autoload includes
    require_once DOIG_PLUGIN_DIR . 'includes/class-license.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-settings.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-cache.php';
    // Shared-logging component (vendored from _shared/shared-logging via its
    // sync.sh - never edit these three in place). Booted immediately so cron,
    // REST and front-end paths can log; the level getter reads the live
    // setting on every write. Log: uploads/dynamic-og-image-generator/
    // dynamic-og-image-generator.log (the cross-plugin standard since 2.5.0;
    // pre-2.5.0 installs' old doig.log sits in the image-cache dir, removed
    // with it at uninstall).
    require_once DOIG_PLUGIN_DIR . 'includes/class-log.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-logger.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-log-ui.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-site-notices.php';
    // VENDORED shared sitewide-findings banner (re-sync via _shared/shared-site-notices)
    DOIG_Log::boot( array(
        'slug'             => DOIG_PLUGIN_SLUG,
        'prefix'           => 'doig',
        'log_level_getter' => function () {
            return DOIG_Settings::get( 'log_level', 'info' );
        },
        'option_key'       => DOIG_OPTION_KEY,
        'settings_slug'    => 'doig-settings',
    ) );
    require_once DOIG_PLUGIN_DIR . 'includes/class-screenshot.php';
    // after logger
    require_once DOIG_PLUGIN_DIR . 'includes/class-source-resolver.php';
    // 2.4.0: image-source resolver (pure, no init hook needed)
    require_once DOIG_PLUGIN_DIR . 'includes/class-surfaces.php';
    // 2.7.0 M1: non-post surface registry (synthetic IDs)
    require_once DOIG_PLUGIN_DIR . 'includes/class-surface-meta.php';
    // 2.7.0 M1: surface state storage shim
    // Premium-only files — stripped from the WP.org free build by the Freemius
    // deployment tool. The file_exists() checks ensure clean degradation when the
    // files are absent. Load before class-cron.php so the doig_screenshot_saved
    // action and all screenshot filters are registered before any cron jobs fire.
    // class-settings__premium_only.php registers the doig_sanitise_settings and
    // doig_settings_get_all filter callbacks that add premium-only fields back.
    if ( file_exists( DOIG_PLUGIN_DIR . 'includes/class-settings__premium_only.php' ) ) {
        require_once DOIG_PLUGIN_DIR . 'includes/class-settings__premium_only.php';
    }
    if ( file_exists( DOIG_PLUGIN_DIR . 'includes/class-media-library__premium_only.php' ) ) {
        require_once DOIG_PLUGIN_DIR . 'includes/class-media-library__premium_only.php';
    }
    if ( file_exists( DOIG_PLUGIN_DIR . 'includes/class-screenshot__premium_only.php' ) ) {
        require_once DOIG_PLUGIN_DIR . 'includes/class-screenshot__premium_only.php';
    }
    if ( file_exists( DOIG_PLUGIN_DIR . 'includes/class-post-meta__premium_only.php' ) ) {
        require_once DOIG_PLUGIN_DIR . 'includes/class-post-meta__premium_only.php';
    }
    if ( file_exists( DOIG_PLUGIN_DIR . 'includes/class-cpts__premium_only.php' ) ) {
        require_once DOIG_PLUGIN_DIR . 'includes/class-cpts__premium_only.php';
    }
    // Repoints the update "homepage" URL away from wp.freemius.com.
    // Strictly premium-only: WP.org Plugin Check flags the update-check
    // transient hook used inside as a "Plugin Updater detected" severity-9
    // ERROR in any free-build file, so the hook name must not appear
    // anywhere in the free build (including comments). Premium build needs
    // it because the SDK delivers updates from wp.freemius.com; the free
    // build updates from wp.org so there is nothing to repoint. See
    // _standards/docs/CONVENTIONS.md § "Update-notice homepage URL".
    if ( file_exists( DOIG_PLUGIN_DIR . 'includes/class-updater-url__premium_only.php' ) ) {
        require_once DOIG_PLUGIN_DIR . 'includes/class-updater-url__premium_only.php';
    }
    require_once DOIG_PLUGIN_DIR . 'includes/class-cron.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-post-meta.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-proxy-callback.php';
    // REST endpoint for async proxy callbacks
    require_once DOIG_PLUGIN_DIR . 'includes/class-og-injector.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-seo-compat.php';
    require_once DOIG_PLUGIN_DIR . 'includes/class-plugin.php';
    if ( is_admin() ) {
        require_once DOIG_PLUGIN_DIR . 'admin/class-admin.php';
        require_once DOIG_PLUGIN_DIR . 'admin/class-error-guidance.php';
        // 2.6.2: per-code failure guidance map
        require_once DOIG_PLUGIN_DIR . 'admin/class-gutenberg.php';
        require_once DOIG_PLUGIN_DIR . 'admin/class-list-actions.php';
        if ( file_exists( DOIG_PLUGIN_DIR . 'admin/class-admin__premium_only.php' ) ) {
            require_once DOIG_PLUGIN_DIR . 'admin/class-admin__premium_only.php';
        }
    }
    // Activation / deactivation hooks
    register_activation_hook( __FILE__, array('DOIG_Plugin', 'activate') );
    register_deactivation_hook( __FILE__, array('DOIG_Plugin', 'deactivate') );
    // Boot
    add_action( 'plugins_loaded', array('DOIG_Plugin', 'init') );
    // Freemius uninstall hook — replaces uninstall.php (Freemius requires this pattern).
    // Registered after all requires so DOIG_Post_Meta::KEYS is available at call time.
    //
    // The function_exists guard is belt-and-braces against the brief window
    // where both free and premium plugin folders co-exist. The outer
    // set_basename guard above is the primary defence; this second guard
    // makes the function declaration idempotent in case the outer guard
    // is bypassed somehow (e.g. recovery-mode plugin loading).
    if ( !function_exists( 'doig_fs_uninstall_cleanup' ) ) {
        doig_fs()->add_action( 'after_uninstall', 'doig_fs_uninstall_cleanup' );
        function doig_fs_uninstall_cleanup() {
            // Remove global settings and queue options
            delete_option( 'doig_settings' );
            delete_option( 'doig_regen_queue' );
            delete_option( 'doig_submission_retry' );
            delete_option( 'doig_proxy_hmac_secret' );
            // 2.6.0: weekly favicon check state.
            delete_option( 'doig_favicon_missing' );
            delete_option( 'doig_favicon_checked_at' );
            // 2.6.2: buried-diagnostics signals.
            delete_option( 'doig_callback_blocked_at' );
            delete_option( 'doig_queue_last_run_at' );
            // Per-user notice dismissals (delete_metadata with $delete_all
            // removes the key for every user in one call).
            delete_metadata(
                'user',
                0,
                '_doig_pgp_notice_dismissed',
                '',
                true
            );
            delete_metadata(
                'user',
                0,
                '_doig_favicon_notice_dismissed_at',
                '',
                true
            );
            // Remove all post meta - keep in sync with DOIG_Post_Meta::KEYS.
            // Additional meta keys registered by separately-distributed builds
            // hook the doig_uninstall_meta_keys filter to extend this list.
            $meta_keys = (array) apply_filters( 'doig_uninstall_meta_keys', array(
                '_doig_enabled',
                '_doig_status',
                '_doig_cached_url',
                '_doig_cached_at',
                '_doig_scheduled_at',
                '_doig_job_id',
                '_doig_error_code',
                '_doig_error_message',
                // 2.4.0: Image Source feature.
                '_doig_source',
                '_doig_custom_image_id',
                // 2.4.0 M5: cache-flip orphan sweep tracker.
                '_doig_orphaned_at',
                // 2.6.2: capture-trigger provenance stamped at enqueue.
                '_doig_capture_trigger',
            ) );
            foreach ( $meta_keys as $key ) {
                delete_post_meta_by_key( $key );
            }
            // Remove cached screenshot directory and everything inside it.
            //
            // Uses WP_Filesystem rather than glob+wp_delete_file+rmdir because
            // the WordPress.org Plugin Check scanner does not honour
            // phpcs:ignore for the alternative-function rule; the only way to
            // clear it is to actually use the WP_Filesystem API. The recursive
            // $wp_filesystem->delete() replaces the whole previous loop+rmdir
            // dance with a single call.
            //
            // If WP_Filesystem fails to initialise (rare edge-case hosts with
            // no FTP creds and no direct write), the cache directory may be
            // left behind. This is preferable to a hard failure during
            // uninstall — the orphan directory is harmless and the user can
            // remove it manually.
            $upload_dir = wp_upload_dir();
            $cache_dir = trailingslashit( $upload_dir['basedir'] ) . 'dynamic-og-images/';
            // 2.5.0: the shared-logging component's log directory (the
            // cross-plugin uploads/<slug>/ standard). The cache dir above also
            // covers the pre-2.5.0 legacy doig.log that lived inside it.
            $log_dir = trailingslashit( $upload_dir['basedir'] ) . 'dynamic-og-image-generator/';
            foreach ( array($cache_dir, $log_dir) as $doig_dir ) {
                if ( is_dir( $doig_dir ) ) {
                    if ( !function_exists( 'WP_Filesystem' ) ) {
                        require_once ABSPATH . 'wp-admin/includes/file.php';
                    }
                    WP_Filesystem();
                    global $wp_filesystem;
                    if ( $wp_filesystem ) {
                        $wp_filesystem->delete( $doig_dir, true, 'd' );
                    }
                }
            }
            // Clear scheduled cron events
            wp_clear_scheduled_hook( 'doig_daily_refresh_check' );
            wp_clear_scheduled_hook( 'doig_take_screenshot' );
            wp_clear_scheduled_hook( 'doig_process_queue' );
            // 2.4.0 M5: cache-flip orphan sweep.
            wp_clear_scheduled_hook( 'doig_orphan_sweep' );
            // 2.4.1: dedicated polling pass.
            wp_clear_scheduled_hook( 'doig_poll_pending' );
        }

    }
    // end if ( ! function_exists( 'doig_fs_uninstall_cleanup' ) )
    // Add Settings + Docs links to the action row on the Plugins list page.
    // array_unshift() prepends in the order Docs, Settings — so Settings ends
    // up before Docs in the rendered row (most-used link on the left).
    add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), function ( $links ) {
        $docs = '<a href="' . esc_url( DOIG_Plugin::docs_url( '', 'plugin_action_link' ) ) . '" target="_blank" rel="noopener">' . esc_html__( 'Docs', 'dynamic-og-image-generator' ) . '</a>';
        // 2.4.0: top-level menu, Settings is a sub-page.
        $settings = '<a href="' . esc_url( admin_url( 'admin.php?page=doig-settings' ) ) . '">' . esc_html__( 'Settings', 'dynamic-og-image-generator' ) . '</a>';
        array_unshift( $links, $docs );
        array_unshift( $links, $settings );
        return $links;
    } );
    // Add a "Documentation" link to the meta row on the right of the
    // Plugins list page (alongside Author | View details). Lower-prominence
    // than the action link but the WordPress-native location for reference
    // material that doesn't belong in the actions column.
    add_filter(
        'plugin_row_meta',
        function ( $links, $file ) {
            if ( plugin_basename( __FILE__ ) !== $file ) {
                return $links;
            }
            $links[] = '<a href="' . esc_url( DOIG_Plugin::docs_url( '', 'plugin_row_meta' ) ) . '" target="_blank" rel="noopener">' . esc_html__( 'Documentation', 'dynamic-og-image-generator' ) . '</a>';
            return $links;
        },
        10,
        2
    );
}
// end of else (first-load branch — full bootstrap)