bbadrs_is_pro(); $cleaner_url = admin_url('admin.php?page=' . self::MENU_SLUG); $how_url = admin_url('admin.php?page=' . self::MENU_SLUG . '-how-it-works'); $box = 'background:#fff;border:1px solid #c3c4c7;padding:18px 22px;margin:14px 0;box-shadow:0 1px 1px rgba(0,0,0,.04);'; echo '
'; echo '

' . esc_html__('Getting Started - AI Data Remover', 'ai-data-remover-seo-cleanup-tool') . '

'; echo '

' . esc_html__('AI Data Remover finds the leftover data-* HTML attributes that AI writing tools, page builders, and copy-paste inject into your content, then lets you bulk-remove them on demand. It only ever edits post_content - never theme files, CSS, JavaScript, options, or meta. Follow the four steps below for your first clean-up.', 'ai-data-remover-seo-cleanup-tool') . '

'; // Step-by-step echo '
'; echo '

' . esc_html__('Step-by-step first run', 'ai-data-remover-seo-cleanup-tool') . '

'; echo '
    '; echo '
  1. ' . esc_html__('Install & activate.', 'ai-data-remover-seo-cleanup-tool') . ' ' . esc_html__('You have already done this - the plugin adds the "AI Data Remover" top-level menu (shield icon) in wp-admin.', 'ai-data-remover-seo-cleanup-tool') . '
  2. '; echo '
  3. ' . esc_html__('Back up first (strongly recommended).', 'ai-data-remover-seo-cleanup-tool') . ' ' . esc_html__('Bulk removal rewrites post_content and there is no built-in undo. Take a database backup, or test on staging, before your first run. If post revisions are enabled you can roll back a single post from its revision history.', 'ai-data-remover-seo-cleanup-tool') . '
  4. '; echo '
  5. '; printf( wp_kses( /* translators: %s = link to the Cleaner page (HTML anchor) */ __('Open the tool. There is no separate settings page - everything happens on %s. Choose the content type (Posts or Pages on Free), then pick a scan mode: "All" strips every data-* attribute, "Specific" targets a single attribute by name (e.g. type "elementor-type" to match only data-elementor-type).', 'ai-data-remover-seo-cleanup-tool'), array('strong' => array(), 'a' => array('href' => array())) ), '' . esc_html__('AI Data Remover -> Cleaner', 'ai-data-remover-seo-cleanup-tool') . '' ); echo '
  6. '; echo '
  7. ' . esc_html__('Scan, review, remove.', 'ai-data-remover-seo-cleanup-tool') . ' ' . esc_html__('Click Scan to list matching items with a per-item match count. Tick the rows you want cleaned and click "Remove Selected". Free removes up to 25 items per run; scan up to 500 items per post type.', 'ai-data-remover-seo-cleanup-tool') . '
  8. '; echo '
'; echo '
'; // You're done / verify echo '
'; echo '

' . esc_html__("You're done - verify it worked", 'ai-data-remover-seo-cleanup-tool') . '

'; echo ''; echo '
'; if (!$is_pro) { echo '
'; echo '

' . esc_html__('Working past the Free limits', 'ai-data-remover-seo-cleanup-tool') . '

'; echo '

' . esc_html__('Free is capped at Posts & Pages, a 500-item scan window, 25 removals per run, and 50 rows per page. Pro removes those caps and adds a Select-All checkbox, WooCommerce products + public custom post types, CSV export of scan results, and hides the upgrade notice - all gated on a valid BBA license.', 'ai-data-remover-seo-cleanup-tool') . '

'; echo '

' . esc_html__('See Pro', 'ai-data-remover-seo-cleanup-tool') . '

'; echo '
'; } echo '

'; echo '' . esc_html__('Open the Cleaner ->', 'ai-data-remover-seo-cleanup-tool') . '  '; echo '' . esc_html__('How It Works ->', 'ai-data-remover-seo-cleanup-tool') . ''; echo '

'; echo '
'; } /** * How It Works - per-feature explanation (what it does / how to use / Pro). */ public function bbadrs_render_how_it_works_page() { if (!current_user_can(self::CAP)) { return; } $is_pro = $this->bbadrs_is_pro(); $box = 'background:#fff;border:1px solid #c3c4c7;padding:18px 22px;margin:14px 0;box-shadow:0 1px 1px rgba(0,0,0,.04);'; echo '
'; echo '

' . esc_html__('How It Works - AI Data Remover', 'ai-data-remover-seo-cleanup-tool') . '

'; echo '

' . esc_html__('What each feature does, how to use it, and where Pro extends it. AI Data Remover changes only post_content - themes, plugins, CSS, JavaScript, options, and meta are never touched.', 'ai-data-remover-seo-cleanup-tool') . '

'; $features = array( array( __('Scan Posts & Pages', 'ai-data-remover-seo-cleanup-tool'), __('Queries your database for any post or page whose content contains "data-" and lists each match with a per-item match count. Results are cached for 10 minutes so the admin page stays fast; the cache clears automatically after a removal.', 'ai-data-remover-seo-cleanup-tool'), __('Choose Posts or Pages, then click Scan. Read the match count column to see which items carry the most leftover attributes.', 'ai-data-remover-seo-cleanup-tool'), ), array( __('Targeted or Universal mode', 'ai-data-remover-seo-cleanup-tool'), __('"All" mode strips every data-* attribute in one pass. "Specific" mode targets a single named attribute. Custom input is sanitised to letters, digits, and hyphens, so you can never match outside the data-* namespace.', 'ai-data-remover-seo-cleanup-tool'), __('Pick "Specific" and type an attribute name without the data- prefix (e.g. "id", "elementor-type") to clean just that one; leave it on "All" for a full sweep.', 'ai-data-remover-seo-cleanup-tool'), ), array( __('Manual bulk removal', 'ai-data-remover-seo-cleanup-tool'), __('Nothing is ever removed automatically. You scan, review, tick rows, and submit. The remove action requires a valid WordPress nonce and the manage_options capability, then rewrites only the matched attributes out of post_content.', 'ai-data-remover-seo-cleanup-tool'), __('Tick the rows you want, click "Remove Selected", and re-scan to confirm. Free removes up to 25 items per run.', 'ai-data-remover-seo-cleanup-tool'), ), array( __('Free limits', 'ai-data-remover-seo-cleanup-tool'), __('The Free tier enforces three hard caps to stay safe and quick on shared hosting: a 500-item scan window per post type, 25 removals per submitted run, and up to 50 rows per page.', 'ai-data-remover-seo-cleanup-tool'), __('Work in batches: scan, remove 25, re-scan, repeat. Pro removes all three caps.', 'ai-data-remover-seo-cleanup-tool'), ), array( __('No data leaves your site', 'ai-data-remover-seo-cleanup-tool'), __('The Free plugin makes zero external HTTP requests - no calls to bigbad.agency, WordPress.org, or any analytics endpoint. No telemetry, no phone-home. Your content stays in your own database.', 'ai-data-remover-seo-cleanup-tool'), __('Nothing to configure - this is always on.', 'ai-data-remover-seo-cleanup-tool'), ), ); foreach ($features as $f) { echo '
'; echo '

' . esc_html($f[0]) . '

'; echo '

' . esc_html__('What it does:', 'ai-data-remover-seo-cleanup-tool') . ' ' . esc_html($f[1]) . '

'; echo '

' . esc_html__('How to use it:', 'ai-data-remover-seo-cleanup-tool') . ' ' . esc_html($f[2]) . '

'; echo '
'; } // Pro features (honest) echo '
'; echo '

' . esc_html__('What Pro adds', 'ai-data-remover-seo-cleanup-tool'); echo ' PRO

'; echo '

' . esc_html__('Every Pro behaviour below is gated on a valid BBA license - none of it unlocks on plugin activation alone.', 'ai-data-remover-seo-cleanup-tool') . '

'; echo ''; if (!$is_pro) { echo '

' . esc_html__('See Pro pricing', 'ai-data-remover-seo-cleanup-tool') . '

'; } else { echo '

' . esc_html__('Pro is active on this site.', 'ai-data-remover-seo-cleanup-tool') . '

'; } echo '
'; echo '
'; } public function bbadrs_render_pro_notice() { if ($this->bbadrs_is_pro()) { return; } $page = ''; if (isset($_GET['page'])) { $page = sanitize_key(wp_unslash($_GET['page'])); } if ($page !== self::MENU_SLUG) { return; } echo "

"; echo '' . esc_html__('AI Data Remover (Free)', 'ai-data-remover-seo-cleanup-tool') . ' '; echo esc_html__('is limited to Posts & Pages, 500 items, and 25 removals per run.', 'ai-data-remover-seo-cleanup-tool') . ' '; echo esc_html__('Upgrade to PRO to unlock WooCommerce, Select-All, CSV export, and unlimited cleaning.', 'ai-data-remover-seo-cleanup-tool'); echo "

"; } public function bbadrs_admin_enqueue($hook_suffix) { // Only load on this plugin's admin page. if ($hook_suffix !== 'toplevel_page_' . self::MENU_SLUG) { return; } $asset_url = plugin_dir_url(__FILE__) . 'assets/'; wp_register_script( 'bbadrs-ai-drm-admin', $asset_url . 'admin.js', array(), self::VERSION, true ); // Optional PRO flag for UI behavior wp_add_inline_script( 'bbadrs-ai-drm-admin', 'window.BBADRS_AI_DRM = ' . wp_json_encode(array( 'isPro' => $this->bbadrs_is_pro() ? 1 : 0, )) . ';', 'before' ); wp_enqueue_script('bbadrs-ai-drm-admin'); wp_register_style( 'bbadrs-ai-drm-admin', $asset_url . 'admin.css', array(), self::VERSION ); wp_enqueue_style('bbadrs-ai-drm-admin'); } private function bbadrs_build_regex($mode, $q) { $mode = ($mode === 'custom') ? 'custom' : 'all'; if ($mode === 'custom' && $q) { $q = str_replace('data-', '', (string) $q); $q = preg_replace('/[^a-z0-9\-]/i', '', $q); return '/\sdata-' . preg_quote($q, '/') . '="[^"]*"/'; } return '/\sdata-[a-zA-Z0-9\-]+="[^"]*"/'; } private function bbadrs_clean_content($content, $regex) { return preg_replace($regex, '', (string) $content); } /** * Cached ID scan to avoid repeated direct queries on every page load. */ private function bbadrs_get_candidate_ids($post_type) { global $wpdb; $post_type = (string) $post_type; $cache_key = 'bbadrs_ai_drm_ids_' . $post_type; $cached = wp_cache_get($cache_key, self::CACHE_GROUP); if ($cached !== false && is_array($cached)) { return $cached; } // Direct query is acceptable here; we mitigate with caching. $ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type=%s AND post_status IN('publish','draft','private') AND post_content LIKE %s ORDER BY post_modified DESC", $post_type, '%data-%' ) ); if (!is_array($ids)) { $ids = array(); } // Cache for 10 minutes (keeps admin snappy while still updating regularly) wp_cache_set($cache_key, $ids, self::CACHE_GROUP, 10 * MINUTE_IN_SECONDS); return $ids; } public function bbadrs_render_admin_page() { if (!current_user_can(self::CAP)) { return; } $is_pro = $this->bbadrs_is_pro(); $types = $this->bbadrs_allowed_post_types(); // Nonce for admin screen interactions (GET + POST) $screen_nonce = wp_create_nonce(self::NONCE_ACTION); // Sanitize GET inputs $post_type = isset($types[0]) ? $types[0] : 'post'; if (isset($_GET['post_type'])) { $post_type = sanitize_key(wp_unslash($_GET['post_type'])); } if (!in_array($post_type, $types, true)) { $post_type = isset($types[0]) ? $types[0] : 'post'; } $pattern = 'all'; if (isset($_GET['pattern'])) { $pattern = sanitize_key(wp_unslash($_GET['pattern'])); } $pattern = ($pattern === 'custom') ? 'custom' : 'all'; $q = ''; if (isset($_GET['q'])) { $q = sanitize_text_field(wp_unslash($_GET['q'])); } // Optional nonce verification for GET-driven scanning. if (isset($_GET['_bbadrs_nonce'])) { $maybe_nonce = sanitize_text_field(wp_unslash($_GET['_bbadrs_nonce'])); if (!wp_verify_nonce($maybe_nonce, self::NONCE_ACTION)) { echo "

" . esc_html__('Security check failed. Please reload and try again.', 'ai-data-remover-seo-cleanup-tool') . "

"; return; } } $regex = $this->bbadrs_build_regex($pattern, $q); $per_page = 25; if (isset($_GET['per_page'])) { $per_page = (int) wp_unslash($_GET['per_page']); } $per_page = min(200, max(10, $per_page)); if (!$is_pro) { $per_page = min(self::FREE_MAX_PER_PAGE, $per_page); } $paged = 1; if (isset($_GET['paged'])) { $paged = (int) wp_unslash($_GET['paged']); } $paged = max(1, $paged); // Candidate scan (cached) $ids = $this->bbadrs_get_candidate_ids($post_type); if (!$is_pro) { $ids = array_slice($ids, 0, self::FREE_MAX_SCAN); } $total = count($ids); $pages = (int) ceil($total / $per_page); $slice = array_slice($ids, ($paged - 1) * $per_page, $per_page); // Handle POST bulk clean if (!empty($_POST['bbadrs_ai_action'])) { check_admin_referer(self::NONCE_ACTION); $selected = array_map('intval', (array) (isset($_POST['bbadrs_ids']) ? $_POST['bbadrs_ids'] : array())); if (!$is_pro) { $selected = array_slice($selected, 0, self::FREE_MAX_BULK); } foreach ($selected as $id) { $p = get_post($id); if (!$p) { continue; } wp_update_post(array( 'ID' => $id, 'post_content' => $this->bbadrs_clean_content($p->post_content, $regex), )); } // Clear cached IDs since content changed wp_cache_delete('bbadrs_ai_drm_ids_' . $post_type, self::CACHE_GROUP); /* translators: %d is the number of items (posts / pages / products) cleaned in this batch. */ echo "

" . esc_html(sprintf(__('Cleaned %d items.', 'ai-data-remover-seo-cleanup-tool'), count($selected))) . "

"; } echo "

" . esc_html__('AI Data Remover', 'ai-data-remover-seo-cleanup-tool') . "

"; // Scan form (GET) echo "
"; echo ""; echo ""; echo " "; echo " "; echo " "; echo ""; echo "
"; // Results form (POST) echo "
"; wp_nonce_field(self::NONCE_ACTION); echo ""; echo ""; echo ""; if ($is_pro) { echo ""; } else { echo ""; } echo ""; echo ""; echo ""; foreach ($slice as $id) { $p = get_post((int) $id); if (!$p) { continue; } preg_match_all($regex, $p->post_content, $m); if (empty($m[0])) { continue; } echo ""; echo ""; echo ""; echo ""; echo ""; } echo "
" . esc_html__('PRO', 'ai-data-remover-seo-cleanup-tool') . "" . esc_html__('Post', 'ai-data-remover-seo-cleanup-tool') . "" . esc_html__('Matches', 'ai-data-remover-seo-cleanup-tool') . "
" . esc_html($p->post_title) . "" . esc_html((string) count($m[0])) . "
"; echo "

"; echo "
"; } } new BBADRS_AI_Data_Remover_SEO(); // Free-tier Pro notice: one dismissible notice on the Plugins screen and this plugin's own pages. // No Pro code here; hidden once Pro is active or the notice is dismissed. if ( is_admin() ) { require_once __DIR__ . '/includes/class-pro-upgrade-notice.php'; BBADRS_Pro_Upgrade_Notice::register( array( 'id' => 'ai-data-remover-seo-cleanup-tool', 'url' => 'https://bigbad.agency/plugins/ai-data-remover-seo-cleanup-tool/?utm_source=wp-admin&utm_medium=plugin-notice&utm_campaign=ai-data-remover-seo-cleanup-tool', 'page_prefixes' => array( 'ai-data-remover' ), 'pro_filter' => 'bbadrs_ai_drm_is_pro', 'strings' => array( 'headline' => __( 'AI Data Remover Pro', 'ai-data-remover-seo-cleanup-tool' ), 'body' => __( 'Pro scans every public post type including WooCommerce products, lifts the per-scan and per-run limits, and exports results to CSV. Pro is an annual license, per site.', 'ai-data-remover-seo-cleanup-tool' ), 'cta' => __( 'See what Pro adds', 'ai-data-remover-seo-cleanup-tool' ), 'dismiss' => __( 'Dismiss', 'ai-data-remover-seo-cleanup-tool' ), ), ) ); }