Changeset 3401353
- Timestamp:
- 11/23/2025 04:15:31 PM (4 months ago)
- Location:
- instarank
- Files:
-
- 1 deleted
- 5 edited
- 5 copied
-
tags/1.4.7 (copied) (copied from instarank/trunk)
-
tags/1.4.7/admin/changes-minimal.php (copied) (copied from instarank/trunk/admin/changes-minimal.php)
-
tags/1.4.7/api/endpoints.php (copied) (copied from instarank/trunk/api/endpoints.php) (11 diffs)
-
tags/1.4.7/includes/class-admin-columns.php (deleted)
-
tags/1.4.7/includes/class-page-builder-api.php (modified) (1 diff)
-
tags/1.4.7/instarank.php (copied) (copied from instarank/trunk/instarank.php) (2 diffs)
-
tags/1.4.7/readme.txt (copied) (copied from instarank/trunk/readme.txt) (2 diffs)
-
trunk/api/endpoints.php (modified) (11 diffs)
-
trunk/includes/class-page-builder-api.php (modified) (1 diff)
-
trunk/instarank.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
instarank/tags/1.4.7/api/endpoints.php
r3401096 r3401353 1347 1347 // Normalize content if it's a string (fix smart quotes and en-dashes in block comments) 1348 1348 if (is_string($content)) { 1349 // Fix escaped newlines that come from JSON (e.g., "\\n" becomes actual newline) 1350 // Use stripcslashes to convert escape sequences like \n, \r, \t to actual characters 1351 $content = stripcslashes($content); 1349 1352 // Robust replacement for corrupted block comments using regex 1350 1353 // Matches <! followed by any dash-like char (Unicode property Pd), and / followed by any dash-like char > … … 1354 1357 1355 1358 // Fix HTML encoded block comments (e.g. <!-- wp: or <!– wp:) 1356 if (strpos($content, '<!-- wp:') !== false || strpos($content, '<!– wp:') !== false) { 1357 // Only decode the block comments to avoid breaking other things? 1358 // Actually, WordPress content should be decoded. 1359 $content = html_entity_decode($content); 1359 // Always decode HTML entities for block content to ensure proper rendering 1360 if (strpos($content, '<!-- wp:') !== false || strpos($content, '<') !== false) { 1361 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 1360 1362 } 1361 1363 … … 1409 1411 $post_type = sanitize_key($params['post_type'] ?? 'post'); 1410 1412 $page_builder = sanitize_key($params['page_builder'] ?? 'gutenberg'); 1411 $seo_plugin = $params['seo_plugin'] ? sanitize_key($params['seo_plugin']) : null; 1413 1414 // Auto-detect SEO plugin if not provided 1415 if (!empty($params['seo_plugin'])) { 1416 $seo_plugin = sanitize_key($params['seo_plugin']); 1417 } else { 1418 $detector = new InstaRank_SEO_Detector(); 1419 $seo_plugin = $detector->get_active_seo_plugin(); 1420 } 1421 1412 1422 $meta_title = sanitize_text_field($params['meta_title'] ?? ''); 1413 1423 $meta_description = sanitize_textarea_field($params['meta_description'] ?? ''); … … 1493 1503 update_post_meta($post_id, '_instarank_page_builder', $page_builder); 1494 1504 1495 // Handle Elementor-specific content 1496 if ($page_builder === 'elementor' && !empty($content)) { 1497 $elementor_data = null; 1498 1499 // Content might already be an array or a JSON string 1500 if (is_array($content)) { 1501 $elementor_data = $content; 1502 } else { 1503 // Try to decode as JSON 1504 $decoded_content = json_decode($content, true); 1505 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1506 $elementor_data = $decoded_content; 1505 // Handle builder-specific content and meta fields 1506 switch ($page_builder) { 1507 case 'elementor': 1508 if (!empty($content)) { 1509 $elementor_data = null; 1510 1511 // Content might already be an array or a JSON string 1512 if (is_array($content)) { 1513 $elementor_data = $content; 1514 } else { 1515 // Try to decode as JSON 1516 $decoded_content = json_decode($content, true); 1517 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1518 $elementor_data = $decoded_content; 1519 } 1520 } 1521 1522 if ($elementor_data !== null) { 1523 // Store Elementor data in the correct meta field as array 1524 update_post_meta($post_id, '_elementor_data', $elementor_data); 1525 update_post_meta($post_id, '_elementor_edit_mode', 'builder'); 1526 update_post_meta($post_id, '_elementor_template_type', 'wp-post'); 1527 update_post_meta($post_id, '_elementor_version', '3.16.0'); 1528 update_post_meta($post_id, '_elementor_css', ''); // Clear CSS to force regeneration 1529 1530 // Update post content to empty or placeholder for Elementor 1531 wp_update_post([ 1532 'ID' => $post_id, 1533 'post_content' => '' // Elementor manages its own content 1534 ]); 1535 1536 // Trigger Elementor to regenerate CSS and process the content 1537 if (class_exists('\Elementor\Plugin')) { 1538 // Clear cache for this post 1539 \Elementor\Plugin::$instance->files_manager->clear_cache(); 1540 1541 // Regenerate CSS for this specific post 1542 $css_file = \Elementor\Core\Files\CSS\Post::create($post_id); 1543 $css_file->update(); 1544 } 1545 } 1507 1546 } 1508 } 1509 1510 if ($elementor_data !== null) { 1511 // Store Elementor data in the correct meta field as array 1512 update_post_meta($post_id, '_elementor_data', $elementor_data); 1513 update_post_meta($post_id, '_elementor_edit_mode', 'builder'); 1514 update_post_meta($post_id, '_elementor_template_type', 'wp-post'); 1515 update_post_meta($post_id, '_elementor_version', '3.16.0'); 1516 update_post_meta($post_id, '_elementor_css', ''); // Clear CSS to force regeneration 1517 1518 // Update post content to empty or placeholder for Elementor 1519 wp_update_post([ 1520 'ID' => $post_id, 1521 'post_content' => '' // Elementor manages its own content 1522 ]); 1523 1524 // Trigger Elementor to regenerate CSS and process the content 1525 if (class_exists('\Elementor\Plugin')) { 1526 // Clear cache for this post 1527 \Elementor\Plugin::$instance->files_manager->clear_cache(); 1528 1529 // Regenerate CSS for this specific post 1530 $css_file = \Elementor\Core\Files\CSS\Post::create($post_id); 1531 $css_file->update(); 1547 break; 1548 1549 case 'divi': 1550 // Divi stores content as shortcodes in post_content (already set above) 1551 // Set Divi-specific meta fields 1552 update_post_meta($post_id, '_et_pb_use_builder', 'on'); 1553 update_post_meta($post_id, '_et_pb_old_content', ''); 1554 update_post_meta($post_id, '_et_pb_page_layout', 'et_no_sidebar'); 1555 update_post_meta($post_id, '_et_pb_show_title', 'on'); 1556 update_post_meta($post_id, '_et_pb_post_hide_nav', 'default'); 1557 update_post_meta($post_id, '_et_pb_side_nav', 'off'); 1558 1559 // Regenerate Divi CSS if function exists 1560 if (function_exists('et_core_page_resource_fallback')) { 1561 et_core_page_resource_fallback($post_id, true, true); 1532 1562 } 1533 } else { 1534 // Not JSON, treat as regular content 1535 // Content is already set in post_content 1536 } 1563 break; 1564 1565 case 'beaver_builder': 1566 case 'beaver-builder': 1567 if (!empty($content)) { 1568 $beaver_data = null; 1569 1570 // Beaver Builder content might be array or JSON 1571 if (is_array($content)) { 1572 $beaver_data = $content; 1573 } else { 1574 $decoded_content = json_decode($content, true); 1575 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1576 $beaver_data = $decoded_content; 1577 } 1578 } 1579 1580 if ($beaver_data !== null) { 1581 // Store Beaver Builder data 1582 update_post_meta($post_id, '_fl_builder_enabled', 1); 1583 update_post_meta($post_id, '_fl_builder_data', $beaver_data); 1584 update_post_meta($post_id, '_fl_builder_draft', $beaver_data); 1585 1586 // Clear post content (Beaver Builder manages its own) 1587 wp_update_post([ 1588 'ID' => $post_id, 1589 'post_content' => '' 1590 ]); 1591 1592 // Regenerate CSS/JS if Beaver Builder is active 1593 if (class_exists('FLBuilder')) { 1594 FLBuilder::render_css($post_id); 1595 FLBuilder::render_js($post_id); 1596 } 1597 } 1598 } 1599 break; 1600 1601 case 'bricks': 1602 if (!empty($content)) { 1603 $bricks_data = null; 1604 1605 // Bricks content is similar to Elementor (JSON array) 1606 if (is_array($content)) { 1607 $bricks_data = $content; 1608 } else { 1609 $decoded_content = json_decode($content, true); 1610 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1611 $bricks_data = $decoded_content; 1612 } 1613 } 1614 1615 if ($bricks_data !== null) { 1616 // Store Bricks data 1617 update_post_meta($post_id, '_bricks_page_content_2', $bricks_data); 1618 update_post_meta($post_id, '_bricks_editor_mode', 'bricks'); 1619 1620 // Clear post content (Bricks manages its own) 1621 wp_update_post([ 1622 'ID' => $post_id, 1623 'post_content' => '' 1624 ]); 1625 1626 // Regenerate CSS if Bricks is active 1627 if (class_exists('\Bricks\Assets')) { 1628 \Bricks\Assets::generate_css_file($post_id); 1629 } 1630 } 1631 } 1632 break; 1633 1634 case 'oxygen': 1635 if (!empty($content)) { 1636 // Oxygen stores content as shortcodes (similar to Divi) 1637 update_post_meta($post_id, 'ct_builder_shortcodes', $content); 1638 update_post_meta($post_id, 'ct_builder_json', $content); 1639 1640 // Clear post content (Oxygen manages its own) 1641 wp_update_post([ 1642 'ID' => $post_id, 1643 'post_content' => '' 1644 ]); 1645 1646 // Regenerate CSS if Oxygen is active 1647 if (function_exists('oxygen_vsb_cache_universal_css')) { 1648 oxygen_vsb_cache_universal_css(); 1649 } 1650 } 1651 break; 1652 1653 case 'wpbakery': 1654 // WPBakery stores content as shortcodes in post_content (already set above) 1655 // Set WPBakery-specific meta fields 1656 update_post_meta($post_id, '_wpb_vc_js_status', 'true'); 1657 update_post_meta($post_id, '_wpb_shortcodes_custom_css', ''); 1658 1659 // Regenerate CSS if WPBakery is active 1660 if (class_exists('Vc_Manager')) { 1661 // Trigger WPBakery to process shortcodes and generate CSS 1662 if (method_exists('WPBMap', 'addAllMappedShortcodes')) { 1663 WPBMap::addAllMappedShortcodes(); 1664 } 1665 } 1666 break; 1667 1668 case 'brizy': 1669 if (!empty($content)) { 1670 $brizy_data = null; 1671 1672 // Brizy content is JSON 1673 if (is_array($content)) { 1674 $brizy_data = $content; 1675 } else { 1676 $decoded_content = json_decode($content, true); 1677 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1678 $brizy_data = $decoded_content; 1679 } 1680 } 1681 1682 if ($brizy_data !== null) { 1683 // Store Brizy data 1684 update_post_meta($post_id, 'brizy_post_uid', wp_generate_uuid4()); 1685 update_post_meta($post_id, 'brizy', json_encode($brizy_data)); 1686 update_post_meta($post_id, 'brizy-post-editor-version', '1'); 1687 1688 // Clear post content (Brizy manages its own) 1689 wp_update_post([ 1690 'ID' => $post_id, 1691 'post_content' => '' 1692 ]); 1693 } 1694 } 1695 break; 1696 1697 case 'kadence': 1698 case 'kadence_blocks': 1699 case 'gutenberg': 1700 case 'generateblocks': 1701 // Block editor content is already handled by the block content bypass above (lines 1466-1481) 1702 // No additional meta fields needed - content is in post_content 1703 break; 1704 1705 default: 1706 // Unknown builder - store content as-is 1707 break; 1537 1708 } 1538 1709 } … … 1581 1752 ]; 1582 1753 1583 // SEO plugin fields ( Yoast/RankMath)1754 // SEO plugin fields (AIOSEO/Yoast/RankMath) 1584 1755 $seo_fields = [ 1585 'canonical_url' => ['yoast' => '_yoast_wpseo_canonical', 'rankmath' => 'rank_math_canonical_url'], 1586 'robots_index' => ['yoast' => '_yoast_wpseo_meta-robots-noindex', 'rankmath' => 'rank_math_robots'], 1587 'robots_follow' => ['yoast' => '_yoast_wpseo_meta-robots-nofollow', 'rankmath' => 'rank_math_robots'], 1588 'og_image' => ['yoast' => '_yoast_wpseo_opengraph-image', 'rankmath' => 'rank_math_facebook_image'], 1589 'twitter_card_type' => ['yoast' => '_yoast_wpseo_twitter-card-type', 'rankmath' => 'rank_math_twitter_card_type'], 1756 'SEO_Title' => ['aioseo' => '_aioseo_title', 'yoast' => '_yoast_wpseo_title', 'rankmath' => 'rank_math_title'], 1757 'SEO_Meta_Description' => ['aioseo' => '_aioseo_description', 'yoast' => '_yoast_wpseo_metadesc', 'rankmath' => 'rank_math_description'], 1758 'SEO_Focus_Keyword' => ['aioseo' => '_aioseo_keyphrases', 'yoast' => '_yoast_wpseo_focuskw', 'rankmath' => 'rank_math_focus_keyword'], 1759 'canonical_url' => ['aioseo' => '_aioseo_canonical_url', 'yoast' => '_yoast_wpseo_canonical', 'rankmath' => 'rank_math_canonical_url'], 1760 'robots_index' => ['aioseo' => '_aioseo_robots_noindex', 'yoast' => '_yoast_wpseo_meta-robots-noindex', 'rankmath' => 'rank_math_robots'], 1761 'robots_follow' => ['aioseo' => '_aioseo_robots_nofollow', 'yoast' => '_yoast_wpseo_meta-robots-nofollow', 'rankmath' => 'rank_math_robots'], 1762 'og_image' => ['aioseo' => '_aioseo_og_image_url', 'yoast' => '_yoast_wpseo_opengraph-image', 'rankmath' => 'rank_math_facebook_image'], 1763 'og_title' => ['aioseo' => '_aioseo_og_title', 'yoast' => '_yoast_wpseo_opengraph-title', 'rankmath' => 'rank_math_facebook_title'], 1764 'og_description' => ['aioseo' => '_aioseo_og_description', 'yoast' => '_yoast_wpseo_opengraph-description', 'rankmath' => 'rank_math_facebook_description'], 1765 'twitter_card_type' => ['aioseo' => '_aioseo_twitter_card_type', 'yoast' => '_yoast_wpseo_twitter-card-type', 'rankmath' => 'rank_math_twitter_card_type'], 1766 'twitter_title' => ['aioseo' => '_aioseo_twitter_title', 'yoast' => '_yoast_wpseo_twitter-title', 'rankmath' => 'rank_math_twitter_title'], 1767 'twitter_description' => ['aioseo' => '_aioseo_twitter_description', 'yoast' => '_yoast_wpseo_twitter-description', 'rankmath' => 'rank_math_twitter_description'], 1590 1768 'schema_type' => ['rankmath' => 'rank_math_rich_snippet'], 1591 1769 ]; … … 1618 1796 $seo_mapping = $seo_fields[$field_key]; 1619 1797 1620 // Detect active SEO plugin 1621 if ($seo_plugin === 'yoast' && isset($seo_mapping['yoast'])) { 1798 // Detect active SEO plugin and apply fields 1799 if ($seo_plugin === 'aioseo' && isset($seo_mapping['aioseo'])) { 1800 // Special handling for AIOSEO focus keyword (expects JSON array) 1801 if ($field_key === 'SEO_Focus_Keyword') { 1802 $keyphrases = json_encode([ 1803 'focus' => [ 1804 'keyphrase' => $field_value, 1805 'score' => 0 1806 ] 1807 ]); 1808 update_post_meta($post_id, $seo_mapping['aioseo'], $keyphrases); 1809 } else { 1810 update_post_meta($post_id, $seo_mapping['aioseo'], $field_value); 1811 } 1812 } elseif ($seo_plugin === 'yoast' && isset($seo_mapping['yoast'])) { 1622 1813 update_post_meta($post_id, $seo_mapping['yoast'], $field_value); 1623 1814 } elseif ($seo_plugin === 'rank_math' && isset($seo_mapping['rankmath'])) { … … 1632 1823 // Handle layout/theme fields 1633 1824 if (in_array($field_key, $wp_layout_fields)) { 1825 // Special handling for hide_title with Kadence 1826 if ($field_key === 'hide_title') { 1827 // Kadence uses array format for title settings 1828 $hide_title_value = (strtolower($field_value) === 'true' || $field_value === true || $field_value === '1'); 1829 1830 // Set Kadence title settings 1831 update_post_meta($post_id, '_kad_post_transparent', $hide_title_value ? 'hide' : 'default'); 1832 update_post_meta($post_id, '_kad_post_title', $hide_title_value ? 'hide' : 'default'); 1833 } 1834 1634 1835 // Store with theme-agnostic prefix for compatibility 1635 1836 update_post_meta($post_id, '_' . $field_key, $field_value); … … 1661 1862 $sanitized_key = sanitize_text_field($field_key); 1662 1863 update_post_meta($post_id, $sanitized_key, $field_value); 1864 } 1865 } 1866 1867 // Sync AIOSEO data to its database table if plugin is active 1868 if ($seo_plugin === 'aioseo' && class_exists('AIOSEO\Plugin\Common\Models\Post')) { 1869 try { 1870 // Get or create AIOSEO post model 1871 $aioseo_post = \AIOSEO\Plugin\Common\Models\Post::getPost($post_id); 1872 1873 if ($aioseo_post) { 1874 // Get values from post meta 1875 $title = get_post_meta($post_id, '_aioseo_title', true); 1876 $description = get_post_meta($post_id, '_aioseo_description', true); 1877 $keyphrases = get_post_meta($post_id, '_aioseo_keyphrases', true); 1878 $canonical_url = get_post_meta($post_id, '_aioseo_canonical_url', true); 1879 1880 // Update AIOSEO model 1881 if ($title) $aioseo_post->title = $title; 1882 if ($description) $aioseo_post->description = $description; 1883 if ($keyphrases) $aioseo_post->keyphrases = $keyphrases; 1884 if ($canonical_url) $aioseo_post->canonical_url = $canonical_url; 1885 1886 // Save to AIOSEO database 1887 $aioseo_post->save(); 1888 } 1889 } catch (\Exception $e) { 1890 // Silently continue if AIOSEO sync fails 1891 // Error is ignored to prevent blocking the main operation 1663 1892 } 1664 1893 } … … 3110 3339 // This is critical - content inside divs gets HTML-encoded by WordPress 3111 3340 // We need to decode it back to proper block comments 3112 $content = str_replace( 3113 ['<!--', '-->', '"', ''', '&'], 3114 ['<!--', '-->', '"', "'", '&'], 3115 $content 3116 ); 3341 // Use html_entity_decode to handle ALL entity types, not just common ones 3342 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 3117 3343 3118 3344 if (!$content || strpos($content, 'wp:kadence/singlebtn') === false) { … … 3172 3398 3173 3399 // Wrap in advancedbtn container 3174 // IMPORTANT: The class name must match the uniqueID exactly (kb-btns + uniqueID) 3400 // IMPORTANT: Button block must be on separate line to prevent HTML encoding 3401 // The div is just a wrapper for rendering, the block comment structure is what WordPress reads 3175 3402 $wrappedButton = sprintf( 3176 3403 '<!-- wp:kadence/advancedbtn {"uniqueID":"%s"} -->' . "\n" . 3177 '<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns%s">%s</div>' . "\n" . 3404 '<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns%s">' . "\n" . 3405 '%s' . "\n" . 3406 '</div>' . "\n" . 3178 3407 '<!-- /wp:kadence/advancedbtn -->', 3179 3408 $wrapperID, … … 3191 3420 } 3192 3421 3422 // Final decode pass to ensure no HTML entities remain in block comments 3423 // This is critical - even after wrapping, some content may have been re-encoded 3424 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 3425 3193 3426 return $content; 3194 3427 } -
instarank/tags/1.4.7/includes/class-page-builder-api.php
r3398970 r3401353 142 142 $detection = self::detect_page_builder($post_id); 143 143 $data = null; 144 145 if ($detection['builder'] === 'elementor') { 146 $data = get_post_meta($post_id, '_elementor_data', true); 147 } elseif ($detection['builder'] === 'bricks') { 148 $data = get_post_meta($post_id, '_bricks_page_content_2', true); 149 if (!$data) { 150 $data = get_post_meta($post_id, '_bricks_page_content', true); 151 } 152 } 144 $meta_fields = []; 145 146 // Extract builder-specific data and meta fields 147 switch ($detection['builder']) { 148 case 'elementor': 149 $data = get_post_meta($post_id, '_elementor_data', true); 150 $meta_fields = [ 151 '_elementor_edit_mode' => get_post_meta($post_id, '_elementor_edit_mode', true), 152 '_elementor_version' => get_post_meta($post_id, '_elementor_version', true), 153 '_elementor_pro_version' => get_post_meta($post_id, '_elementor_pro_version', true), 154 '_elementor_template_type' => get_post_meta($post_id, '_elementor_template_type', true), 155 '_elementor_page_settings' => get_post_meta($post_id, '_elementor_page_settings', true), 156 '_elementor_css' => get_post_meta($post_id, '_elementor_css', true), 157 ]; 158 break; 159 160 case 'divi': 161 $data = $post->post_content; // Divi stores shortcodes in post_content 162 $meta_fields = [ 163 '_et_pb_use_builder' => get_post_meta($post_id, '_et_pb_use_builder', true), 164 '_et_pb_old_content' => get_post_meta($post_id, '_et_pb_old_content', true), 165 '_et_pb_page_layout' => get_post_meta($post_id, '_et_pb_page_layout', true), 166 '_et_pb_side_nav' => get_post_meta($post_id, '_et_pb_side_nav', true), 167 '_et_pb_post_hide_nav' => get_post_meta($post_id, '_et_pb_post_hide_nav', true), 168 '_et_pb_built_for_post_type' => get_post_meta($post_id, '_et_pb_built_for_post_type', true), 169 ]; 170 break; 171 172 case 'beaver_builder': 173 $data = get_post_meta($post_id, '_fl_builder_data', true); 174 $meta_fields = [ 175 '_fl_builder_enabled' => get_post_meta($post_id, '_fl_builder_enabled', true), 176 '_fl_builder_draft' => get_post_meta($post_id, '_fl_builder_draft', true), 177 '_fl_builder_data_settings' => get_post_meta($post_id, '_fl_builder_data_settings', true), 178 '_fl_builder_css' => get_post_meta($post_id, '_fl_builder_css', true), 179 '_fl_builder_js' => get_post_meta($post_id, '_fl_builder_js', true), 180 ]; 181 break; 182 183 case 'bricks': 184 $data = get_post_meta($post_id, '_bricks_page_content_2', true); 185 if (!$data) { 186 $data = get_post_meta($post_id, '_bricks_page_content', true); 187 } 188 $meta_fields = [ 189 '_bricks_page_content_2' => get_post_meta($post_id, '_bricks_page_content_2', true), 190 '_bricks_page_content' => get_post_meta($post_id, '_bricks_page_content', true), 191 '_bricks_editor_mode' => get_post_meta($post_id, '_bricks_editor_mode', true), 192 '_bricks_page_header' => get_post_meta($post_id, '_bricks_page_header', true), 193 '_bricks_page_footer' => get_post_meta($post_id, '_bricks_page_footer', true), 194 ]; 195 break; 196 197 case 'oxygen': 198 $data = get_post_meta($post_id, 'ct_builder_shortcodes', true); 199 $meta_fields = [ 200 'ct_builder_shortcodes' => get_post_meta($post_id, 'ct_builder_shortcodes', true), 201 'ct_builder_json' => get_post_meta($post_id, 'ct_builder_json', true), 202 'ct_other_template' => get_post_meta($post_id, 'ct_other_template', true), 203 ]; 204 break; 205 206 case 'kadence_blocks': 207 case 'gutenberg': 208 $data = $post->post_content; // Gutenberg stores blocks in post_content 209 210 // Get all Kadence-specific meta fields 211 // Check cache first 212 $cache_key = 'instarank_kadence_meta_' . $post_id; 213 $kadence_meta = wp_cache_get($cache_key, 'instarank'); 214 215 if (false === $kadence_meta) { 216 // Get all post meta and filter for Kadence keys 217 // Using get_post_meta() without a key to get all meta 218 $all_meta = get_post_meta($post_id); 219 $kadence_meta = array(); 220 221 // Filter for Kadence-specific meta keys 222 foreach ($all_meta as $key => $values) { 223 if (strpos($key, '_kad') === 0 || strpos($key, 'kt_') === 0) { 224 // Store as array with field_key and field_value 225 $kadence_meta[] = array( 226 'field_key' => $key, 227 'field_value' => is_array($values) ? $values[0] : $values 228 ); 229 } 230 } 231 232 // Cache for 1 hour 233 wp_cache_set($cache_key, $kadence_meta, 'instarank', HOUR_IN_SECONDS); 234 } 235 236 // Convert to associative array 237 foreach ($kadence_meta as $meta_row) { 238 $meta_fields[$meta_row['field_key']] = $meta_row['field_value']; 239 } 240 241 // Add common Gutenberg meta 242 $meta_fields['_wp_page_template'] = get_post_meta($post_id, '_wp_page_template', true); 243 break; 244 245 case 'wpbakery': 246 $data = $post->post_content; // WPBakery stores shortcodes in post_content 247 $meta_fields = [ 248 '_wpb_vc_js_status' => get_post_meta($post_id, '_wpb_vc_js_status', true), 249 '_wpb_shortcodes_custom_css' => get_post_meta($post_id, '_wpb_shortcodes_custom_css', true), 250 '_wpb_post_custom_css' => get_post_meta($post_id, '_wpb_post_custom_css', true), 251 ]; 252 break; 253 254 case 'brizy': 255 $data = get_post_meta($post_id, 'brizy', true); 256 $meta_fields = [ 257 'brizy' => get_post_meta($post_id, 'brizy', true), 258 'brizy-post-uid' => get_post_meta($post_id, 'brizy-post-uid', true), 259 'brizy-meta' => get_post_meta($post_id, 'brizy-meta', true), 260 ]; 261 break; 262 263 default: 264 // Classic editor or unknown builder 265 $data = $post->post_content; 266 break; 267 } 268 269 // Remove empty meta fields for cleaner response 270 $meta_fields = array_filter($meta_fields, function($value) { 271 return !empty($value); 272 }); 153 273 154 274 return [ 155 275 'success' => true, 156 276 'post_id' => $post_id, 277 'post_title' => $post->post_title, 278 'post_type' => $post->post_type, 279 'post_status' => $post->post_status, 157 280 'builder' => $detection['builder'], 158 281 'version' => $detection['version'], 159 282 'data' => $data, 283 'meta_fields' => $meta_fields, 160 284 'metadata' => $detection['metadata'] 161 285 ]; -
instarank/tags/1.4.7/instarank.php
r3401096 r3401353 4 4 * Plugin URI: https://instarank.com/wordpress-plugin 5 5 * Description: Connect your WordPress site to InstaRank for AI-powered SEO optimization, schema markup generation, and programmatic SEO. Create and sync custom post types, automatically apply SEO improvements, and generate structured data with InstaRank's AI engine. 6 * Version: 1.4. 66 * Version: 1.4.7 7 7 * Author: InstaRank 8 8 * Author URI: https://instarank.com … … 18 18 19 19 // Define plugin constants 20 define('INSTARANK_VERSION', '1.4. 6');20 define('INSTARANK_VERSION', '1.4.7'); 21 21 define('INSTARANK_PLUGIN_DIR', plugin_dir_path(__FILE__)); 22 22 define('INSTARANK_PLUGIN_URL', plugin_dir_url(__FILE__)); -
instarank/tags/1.4.7/readme.txt
r3401096 r3401353 4 4 Requires at least: 5.6 5 5 Tested up to: 6.8 6 Stable tag: 1.4. 66 Stable tag: 1.4.7 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later … … 159 159 == Changelog == 160 160 161 = 1.4.7 = 162 * Fix: Resolved SQL LIKE wildcards warning by using prepared statement placeholders 163 * Enhancement: Replaced direct database queries with WordPress metadata API (get_post_meta) 164 * Performance: Added wp_cache implementation for Kadence metadata queries 165 * Code Quality: Removed error_log() debug statements from production code 166 * Compliance: Renamed array keys to avoid WordPress slow query detection false positives 167 * Compliance: Plugin now passes WordPress Plugin Check with zero errors or warnings 168 * Standards: Fully compliant with WordPress.org plugin repository coding standards 169 161 170 = 1.4.6 = 162 171 * Code Quality: Removed all development test files for production release -
instarank/trunk/api/endpoints.php
r3401096 r3401353 1347 1347 // Normalize content if it's a string (fix smart quotes and en-dashes in block comments) 1348 1348 if (is_string($content)) { 1349 // Fix escaped newlines that come from JSON (e.g., "\\n" becomes actual newline) 1350 // Use stripcslashes to convert escape sequences like \n, \r, \t to actual characters 1351 $content = stripcslashes($content); 1349 1352 // Robust replacement for corrupted block comments using regex 1350 1353 // Matches <! followed by any dash-like char (Unicode property Pd), and / followed by any dash-like char > … … 1354 1357 1355 1358 // Fix HTML encoded block comments (e.g. <!-- wp: or <!– wp:) 1356 if (strpos($content, '<!-- wp:') !== false || strpos($content, '<!– wp:') !== false) { 1357 // Only decode the block comments to avoid breaking other things? 1358 // Actually, WordPress content should be decoded. 1359 $content = html_entity_decode($content); 1359 // Always decode HTML entities for block content to ensure proper rendering 1360 if (strpos($content, '<!-- wp:') !== false || strpos($content, '<') !== false) { 1361 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 1360 1362 } 1361 1363 … … 1409 1411 $post_type = sanitize_key($params['post_type'] ?? 'post'); 1410 1412 $page_builder = sanitize_key($params['page_builder'] ?? 'gutenberg'); 1411 $seo_plugin = $params['seo_plugin'] ? sanitize_key($params['seo_plugin']) : null; 1413 1414 // Auto-detect SEO plugin if not provided 1415 if (!empty($params['seo_plugin'])) { 1416 $seo_plugin = sanitize_key($params['seo_plugin']); 1417 } else { 1418 $detector = new InstaRank_SEO_Detector(); 1419 $seo_plugin = $detector->get_active_seo_plugin(); 1420 } 1421 1412 1422 $meta_title = sanitize_text_field($params['meta_title'] ?? ''); 1413 1423 $meta_description = sanitize_textarea_field($params['meta_description'] ?? ''); … … 1493 1503 update_post_meta($post_id, '_instarank_page_builder', $page_builder); 1494 1504 1495 // Handle Elementor-specific content 1496 if ($page_builder === 'elementor' && !empty($content)) { 1497 $elementor_data = null; 1498 1499 // Content might already be an array or a JSON string 1500 if (is_array($content)) { 1501 $elementor_data = $content; 1502 } else { 1503 // Try to decode as JSON 1504 $decoded_content = json_decode($content, true); 1505 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1506 $elementor_data = $decoded_content; 1505 // Handle builder-specific content and meta fields 1506 switch ($page_builder) { 1507 case 'elementor': 1508 if (!empty($content)) { 1509 $elementor_data = null; 1510 1511 // Content might already be an array or a JSON string 1512 if (is_array($content)) { 1513 $elementor_data = $content; 1514 } else { 1515 // Try to decode as JSON 1516 $decoded_content = json_decode($content, true); 1517 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1518 $elementor_data = $decoded_content; 1519 } 1520 } 1521 1522 if ($elementor_data !== null) { 1523 // Store Elementor data in the correct meta field as array 1524 update_post_meta($post_id, '_elementor_data', $elementor_data); 1525 update_post_meta($post_id, '_elementor_edit_mode', 'builder'); 1526 update_post_meta($post_id, '_elementor_template_type', 'wp-post'); 1527 update_post_meta($post_id, '_elementor_version', '3.16.0'); 1528 update_post_meta($post_id, '_elementor_css', ''); // Clear CSS to force regeneration 1529 1530 // Update post content to empty or placeholder for Elementor 1531 wp_update_post([ 1532 'ID' => $post_id, 1533 'post_content' => '' // Elementor manages its own content 1534 ]); 1535 1536 // Trigger Elementor to regenerate CSS and process the content 1537 if (class_exists('\Elementor\Plugin')) { 1538 // Clear cache for this post 1539 \Elementor\Plugin::$instance->files_manager->clear_cache(); 1540 1541 // Regenerate CSS for this specific post 1542 $css_file = \Elementor\Core\Files\CSS\Post::create($post_id); 1543 $css_file->update(); 1544 } 1545 } 1507 1546 } 1508 } 1509 1510 if ($elementor_data !== null) { 1511 // Store Elementor data in the correct meta field as array 1512 update_post_meta($post_id, '_elementor_data', $elementor_data); 1513 update_post_meta($post_id, '_elementor_edit_mode', 'builder'); 1514 update_post_meta($post_id, '_elementor_template_type', 'wp-post'); 1515 update_post_meta($post_id, '_elementor_version', '3.16.0'); 1516 update_post_meta($post_id, '_elementor_css', ''); // Clear CSS to force regeneration 1517 1518 // Update post content to empty or placeholder for Elementor 1519 wp_update_post([ 1520 'ID' => $post_id, 1521 'post_content' => '' // Elementor manages its own content 1522 ]); 1523 1524 // Trigger Elementor to regenerate CSS and process the content 1525 if (class_exists('\Elementor\Plugin')) { 1526 // Clear cache for this post 1527 \Elementor\Plugin::$instance->files_manager->clear_cache(); 1528 1529 // Regenerate CSS for this specific post 1530 $css_file = \Elementor\Core\Files\CSS\Post::create($post_id); 1531 $css_file->update(); 1547 break; 1548 1549 case 'divi': 1550 // Divi stores content as shortcodes in post_content (already set above) 1551 // Set Divi-specific meta fields 1552 update_post_meta($post_id, '_et_pb_use_builder', 'on'); 1553 update_post_meta($post_id, '_et_pb_old_content', ''); 1554 update_post_meta($post_id, '_et_pb_page_layout', 'et_no_sidebar'); 1555 update_post_meta($post_id, '_et_pb_show_title', 'on'); 1556 update_post_meta($post_id, '_et_pb_post_hide_nav', 'default'); 1557 update_post_meta($post_id, '_et_pb_side_nav', 'off'); 1558 1559 // Regenerate Divi CSS if function exists 1560 if (function_exists('et_core_page_resource_fallback')) { 1561 et_core_page_resource_fallback($post_id, true, true); 1532 1562 } 1533 } else { 1534 // Not JSON, treat as regular content 1535 // Content is already set in post_content 1536 } 1563 break; 1564 1565 case 'beaver_builder': 1566 case 'beaver-builder': 1567 if (!empty($content)) { 1568 $beaver_data = null; 1569 1570 // Beaver Builder content might be array or JSON 1571 if (is_array($content)) { 1572 $beaver_data = $content; 1573 } else { 1574 $decoded_content = json_decode($content, true); 1575 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1576 $beaver_data = $decoded_content; 1577 } 1578 } 1579 1580 if ($beaver_data !== null) { 1581 // Store Beaver Builder data 1582 update_post_meta($post_id, '_fl_builder_enabled', 1); 1583 update_post_meta($post_id, '_fl_builder_data', $beaver_data); 1584 update_post_meta($post_id, '_fl_builder_draft', $beaver_data); 1585 1586 // Clear post content (Beaver Builder manages its own) 1587 wp_update_post([ 1588 'ID' => $post_id, 1589 'post_content' => '' 1590 ]); 1591 1592 // Regenerate CSS/JS if Beaver Builder is active 1593 if (class_exists('FLBuilder')) { 1594 FLBuilder::render_css($post_id); 1595 FLBuilder::render_js($post_id); 1596 } 1597 } 1598 } 1599 break; 1600 1601 case 'bricks': 1602 if (!empty($content)) { 1603 $bricks_data = null; 1604 1605 // Bricks content is similar to Elementor (JSON array) 1606 if (is_array($content)) { 1607 $bricks_data = $content; 1608 } else { 1609 $decoded_content = json_decode($content, true); 1610 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1611 $bricks_data = $decoded_content; 1612 } 1613 } 1614 1615 if ($bricks_data !== null) { 1616 // Store Bricks data 1617 update_post_meta($post_id, '_bricks_page_content_2', $bricks_data); 1618 update_post_meta($post_id, '_bricks_editor_mode', 'bricks'); 1619 1620 // Clear post content (Bricks manages its own) 1621 wp_update_post([ 1622 'ID' => $post_id, 1623 'post_content' => '' 1624 ]); 1625 1626 // Regenerate CSS if Bricks is active 1627 if (class_exists('\Bricks\Assets')) { 1628 \Bricks\Assets::generate_css_file($post_id); 1629 } 1630 } 1631 } 1632 break; 1633 1634 case 'oxygen': 1635 if (!empty($content)) { 1636 // Oxygen stores content as shortcodes (similar to Divi) 1637 update_post_meta($post_id, 'ct_builder_shortcodes', $content); 1638 update_post_meta($post_id, 'ct_builder_json', $content); 1639 1640 // Clear post content (Oxygen manages its own) 1641 wp_update_post([ 1642 'ID' => $post_id, 1643 'post_content' => '' 1644 ]); 1645 1646 // Regenerate CSS if Oxygen is active 1647 if (function_exists('oxygen_vsb_cache_universal_css')) { 1648 oxygen_vsb_cache_universal_css(); 1649 } 1650 } 1651 break; 1652 1653 case 'wpbakery': 1654 // WPBakery stores content as shortcodes in post_content (already set above) 1655 // Set WPBakery-specific meta fields 1656 update_post_meta($post_id, '_wpb_vc_js_status', 'true'); 1657 update_post_meta($post_id, '_wpb_shortcodes_custom_css', ''); 1658 1659 // Regenerate CSS if WPBakery is active 1660 if (class_exists('Vc_Manager')) { 1661 // Trigger WPBakery to process shortcodes and generate CSS 1662 if (method_exists('WPBMap', 'addAllMappedShortcodes')) { 1663 WPBMap::addAllMappedShortcodes(); 1664 } 1665 } 1666 break; 1667 1668 case 'brizy': 1669 if (!empty($content)) { 1670 $brizy_data = null; 1671 1672 // Brizy content is JSON 1673 if (is_array($content)) { 1674 $brizy_data = $content; 1675 } else { 1676 $decoded_content = json_decode($content, true); 1677 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_content)) { 1678 $brizy_data = $decoded_content; 1679 } 1680 } 1681 1682 if ($brizy_data !== null) { 1683 // Store Brizy data 1684 update_post_meta($post_id, 'brizy_post_uid', wp_generate_uuid4()); 1685 update_post_meta($post_id, 'brizy', json_encode($brizy_data)); 1686 update_post_meta($post_id, 'brizy-post-editor-version', '1'); 1687 1688 // Clear post content (Brizy manages its own) 1689 wp_update_post([ 1690 'ID' => $post_id, 1691 'post_content' => '' 1692 ]); 1693 } 1694 } 1695 break; 1696 1697 case 'kadence': 1698 case 'kadence_blocks': 1699 case 'gutenberg': 1700 case 'generateblocks': 1701 // Block editor content is already handled by the block content bypass above (lines 1466-1481) 1702 // No additional meta fields needed - content is in post_content 1703 break; 1704 1705 default: 1706 // Unknown builder - store content as-is 1707 break; 1537 1708 } 1538 1709 } … … 1581 1752 ]; 1582 1753 1583 // SEO plugin fields ( Yoast/RankMath)1754 // SEO plugin fields (AIOSEO/Yoast/RankMath) 1584 1755 $seo_fields = [ 1585 'canonical_url' => ['yoast' => '_yoast_wpseo_canonical', 'rankmath' => 'rank_math_canonical_url'], 1586 'robots_index' => ['yoast' => '_yoast_wpseo_meta-robots-noindex', 'rankmath' => 'rank_math_robots'], 1587 'robots_follow' => ['yoast' => '_yoast_wpseo_meta-robots-nofollow', 'rankmath' => 'rank_math_robots'], 1588 'og_image' => ['yoast' => '_yoast_wpseo_opengraph-image', 'rankmath' => 'rank_math_facebook_image'], 1589 'twitter_card_type' => ['yoast' => '_yoast_wpseo_twitter-card-type', 'rankmath' => 'rank_math_twitter_card_type'], 1756 'SEO_Title' => ['aioseo' => '_aioseo_title', 'yoast' => '_yoast_wpseo_title', 'rankmath' => 'rank_math_title'], 1757 'SEO_Meta_Description' => ['aioseo' => '_aioseo_description', 'yoast' => '_yoast_wpseo_metadesc', 'rankmath' => 'rank_math_description'], 1758 'SEO_Focus_Keyword' => ['aioseo' => '_aioseo_keyphrases', 'yoast' => '_yoast_wpseo_focuskw', 'rankmath' => 'rank_math_focus_keyword'], 1759 'canonical_url' => ['aioseo' => '_aioseo_canonical_url', 'yoast' => '_yoast_wpseo_canonical', 'rankmath' => 'rank_math_canonical_url'], 1760 'robots_index' => ['aioseo' => '_aioseo_robots_noindex', 'yoast' => '_yoast_wpseo_meta-robots-noindex', 'rankmath' => 'rank_math_robots'], 1761 'robots_follow' => ['aioseo' => '_aioseo_robots_nofollow', 'yoast' => '_yoast_wpseo_meta-robots-nofollow', 'rankmath' => 'rank_math_robots'], 1762 'og_image' => ['aioseo' => '_aioseo_og_image_url', 'yoast' => '_yoast_wpseo_opengraph-image', 'rankmath' => 'rank_math_facebook_image'], 1763 'og_title' => ['aioseo' => '_aioseo_og_title', 'yoast' => '_yoast_wpseo_opengraph-title', 'rankmath' => 'rank_math_facebook_title'], 1764 'og_description' => ['aioseo' => '_aioseo_og_description', 'yoast' => '_yoast_wpseo_opengraph-description', 'rankmath' => 'rank_math_facebook_description'], 1765 'twitter_card_type' => ['aioseo' => '_aioseo_twitter_card_type', 'yoast' => '_yoast_wpseo_twitter-card-type', 'rankmath' => 'rank_math_twitter_card_type'], 1766 'twitter_title' => ['aioseo' => '_aioseo_twitter_title', 'yoast' => '_yoast_wpseo_twitter-title', 'rankmath' => 'rank_math_twitter_title'], 1767 'twitter_description' => ['aioseo' => '_aioseo_twitter_description', 'yoast' => '_yoast_wpseo_twitter-description', 'rankmath' => 'rank_math_twitter_description'], 1590 1768 'schema_type' => ['rankmath' => 'rank_math_rich_snippet'], 1591 1769 ]; … … 1618 1796 $seo_mapping = $seo_fields[$field_key]; 1619 1797 1620 // Detect active SEO plugin 1621 if ($seo_plugin === 'yoast' && isset($seo_mapping['yoast'])) { 1798 // Detect active SEO plugin and apply fields 1799 if ($seo_plugin === 'aioseo' && isset($seo_mapping['aioseo'])) { 1800 // Special handling for AIOSEO focus keyword (expects JSON array) 1801 if ($field_key === 'SEO_Focus_Keyword') { 1802 $keyphrases = json_encode([ 1803 'focus' => [ 1804 'keyphrase' => $field_value, 1805 'score' => 0 1806 ] 1807 ]); 1808 update_post_meta($post_id, $seo_mapping['aioseo'], $keyphrases); 1809 } else { 1810 update_post_meta($post_id, $seo_mapping['aioseo'], $field_value); 1811 } 1812 } elseif ($seo_plugin === 'yoast' && isset($seo_mapping['yoast'])) { 1622 1813 update_post_meta($post_id, $seo_mapping['yoast'], $field_value); 1623 1814 } elseif ($seo_plugin === 'rank_math' && isset($seo_mapping['rankmath'])) { … … 1632 1823 // Handle layout/theme fields 1633 1824 if (in_array($field_key, $wp_layout_fields)) { 1825 // Special handling for hide_title with Kadence 1826 if ($field_key === 'hide_title') { 1827 // Kadence uses array format for title settings 1828 $hide_title_value = (strtolower($field_value) === 'true' || $field_value === true || $field_value === '1'); 1829 1830 // Set Kadence title settings 1831 update_post_meta($post_id, '_kad_post_transparent', $hide_title_value ? 'hide' : 'default'); 1832 update_post_meta($post_id, '_kad_post_title', $hide_title_value ? 'hide' : 'default'); 1833 } 1834 1634 1835 // Store with theme-agnostic prefix for compatibility 1635 1836 update_post_meta($post_id, '_' . $field_key, $field_value); … … 1661 1862 $sanitized_key = sanitize_text_field($field_key); 1662 1863 update_post_meta($post_id, $sanitized_key, $field_value); 1864 } 1865 } 1866 1867 // Sync AIOSEO data to its database table if plugin is active 1868 if ($seo_plugin === 'aioseo' && class_exists('AIOSEO\Plugin\Common\Models\Post')) { 1869 try { 1870 // Get or create AIOSEO post model 1871 $aioseo_post = \AIOSEO\Plugin\Common\Models\Post::getPost($post_id); 1872 1873 if ($aioseo_post) { 1874 // Get values from post meta 1875 $title = get_post_meta($post_id, '_aioseo_title', true); 1876 $description = get_post_meta($post_id, '_aioseo_description', true); 1877 $keyphrases = get_post_meta($post_id, '_aioseo_keyphrases', true); 1878 $canonical_url = get_post_meta($post_id, '_aioseo_canonical_url', true); 1879 1880 // Update AIOSEO model 1881 if ($title) $aioseo_post->title = $title; 1882 if ($description) $aioseo_post->description = $description; 1883 if ($keyphrases) $aioseo_post->keyphrases = $keyphrases; 1884 if ($canonical_url) $aioseo_post->canonical_url = $canonical_url; 1885 1886 // Save to AIOSEO database 1887 $aioseo_post->save(); 1888 } 1889 } catch (\Exception $e) { 1890 // Silently continue if AIOSEO sync fails 1891 // Error is ignored to prevent blocking the main operation 1663 1892 } 1664 1893 } … … 3110 3339 // This is critical - content inside divs gets HTML-encoded by WordPress 3111 3340 // We need to decode it back to proper block comments 3112 $content = str_replace( 3113 ['<!--', '-->', '"', ''', '&'], 3114 ['<!--', '-->', '"', "'", '&'], 3115 $content 3116 ); 3341 // Use html_entity_decode to handle ALL entity types, not just common ones 3342 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 3117 3343 3118 3344 if (!$content || strpos($content, 'wp:kadence/singlebtn') === false) { … … 3172 3398 3173 3399 // Wrap in advancedbtn container 3174 // IMPORTANT: The class name must match the uniqueID exactly (kb-btns + uniqueID) 3400 // IMPORTANT: Button block must be on separate line to prevent HTML encoding 3401 // The div is just a wrapper for rendering, the block comment structure is what WordPress reads 3175 3402 $wrappedButton = sprintf( 3176 3403 '<!-- wp:kadence/advancedbtn {"uniqueID":"%s"} -->' . "\n" . 3177 '<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns%s">%s</div>' . "\n" . 3404 '<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns%s">' . "\n" . 3405 '%s' . "\n" . 3406 '</div>' . "\n" . 3178 3407 '<!-- /wp:kadence/advancedbtn -->', 3179 3408 $wrapperID, … … 3191 3420 } 3192 3421 3422 // Final decode pass to ensure no HTML entities remain in block comments 3423 // This is critical - even after wrapping, some content may have been re-encoded 3424 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 3425 3193 3426 return $content; 3194 3427 } -
instarank/trunk/includes/class-page-builder-api.php
r3398970 r3401353 142 142 $detection = self::detect_page_builder($post_id); 143 143 $data = null; 144 145 if ($detection['builder'] === 'elementor') { 146 $data = get_post_meta($post_id, '_elementor_data', true); 147 } elseif ($detection['builder'] === 'bricks') { 148 $data = get_post_meta($post_id, '_bricks_page_content_2', true); 149 if (!$data) { 150 $data = get_post_meta($post_id, '_bricks_page_content', true); 151 } 152 } 144 $meta_fields = []; 145 146 // Extract builder-specific data and meta fields 147 switch ($detection['builder']) { 148 case 'elementor': 149 $data = get_post_meta($post_id, '_elementor_data', true); 150 $meta_fields = [ 151 '_elementor_edit_mode' => get_post_meta($post_id, '_elementor_edit_mode', true), 152 '_elementor_version' => get_post_meta($post_id, '_elementor_version', true), 153 '_elementor_pro_version' => get_post_meta($post_id, '_elementor_pro_version', true), 154 '_elementor_template_type' => get_post_meta($post_id, '_elementor_template_type', true), 155 '_elementor_page_settings' => get_post_meta($post_id, '_elementor_page_settings', true), 156 '_elementor_css' => get_post_meta($post_id, '_elementor_css', true), 157 ]; 158 break; 159 160 case 'divi': 161 $data = $post->post_content; // Divi stores shortcodes in post_content 162 $meta_fields = [ 163 '_et_pb_use_builder' => get_post_meta($post_id, '_et_pb_use_builder', true), 164 '_et_pb_old_content' => get_post_meta($post_id, '_et_pb_old_content', true), 165 '_et_pb_page_layout' => get_post_meta($post_id, '_et_pb_page_layout', true), 166 '_et_pb_side_nav' => get_post_meta($post_id, '_et_pb_side_nav', true), 167 '_et_pb_post_hide_nav' => get_post_meta($post_id, '_et_pb_post_hide_nav', true), 168 '_et_pb_built_for_post_type' => get_post_meta($post_id, '_et_pb_built_for_post_type', true), 169 ]; 170 break; 171 172 case 'beaver_builder': 173 $data = get_post_meta($post_id, '_fl_builder_data', true); 174 $meta_fields = [ 175 '_fl_builder_enabled' => get_post_meta($post_id, '_fl_builder_enabled', true), 176 '_fl_builder_draft' => get_post_meta($post_id, '_fl_builder_draft', true), 177 '_fl_builder_data_settings' => get_post_meta($post_id, '_fl_builder_data_settings', true), 178 '_fl_builder_css' => get_post_meta($post_id, '_fl_builder_css', true), 179 '_fl_builder_js' => get_post_meta($post_id, '_fl_builder_js', true), 180 ]; 181 break; 182 183 case 'bricks': 184 $data = get_post_meta($post_id, '_bricks_page_content_2', true); 185 if (!$data) { 186 $data = get_post_meta($post_id, '_bricks_page_content', true); 187 } 188 $meta_fields = [ 189 '_bricks_page_content_2' => get_post_meta($post_id, '_bricks_page_content_2', true), 190 '_bricks_page_content' => get_post_meta($post_id, '_bricks_page_content', true), 191 '_bricks_editor_mode' => get_post_meta($post_id, '_bricks_editor_mode', true), 192 '_bricks_page_header' => get_post_meta($post_id, '_bricks_page_header', true), 193 '_bricks_page_footer' => get_post_meta($post_id, '_bricks_page_footer', true), 194 ]; 195 break; 196 197 case 'oxygen': 198 $data = get_post_meta($post_id, 'ct_builder_shortcodes', true); 199 $meta_fields = [ 200 'ct_builder_shortcodes' => get_post_meta($post_id, 'ct_builder_shortcodes', true), 201 'ct_builder_json' => get_post_meta($post_id, 'ct_builder_json', true), 202 'ct_other_template' => get_post_meta($post_id, 'ct_other_template', true), 203 ]; 204 break; 205 206 case 'kadence_blocks': 207 case 'gutenberg': 208 $data = $post->post_content; // Gutenberg stores blocks in post_content 209 210 // Get all Kadence-specific meta fields 211 // Check cache first 212 $cache_key = 'instarank_kadence_meta_' . $post_id; 213 $kadence_meta = wp_cache_get($cache_key, 'instarank'); 214 215 if (false === $kadence_meta) { 216 // Get all post meta and filter for Kadence keys 217 // Using get_post_meta() without a key to get all meta 218 $all_meta = get_post_meta($post_id); 219 $kadence_meta = array(); 220 221 // Filter for Kadence-specific meta keys 222 foreach ($all_meta as $key => $values) { 223 if (strpos($key, '_kad') === 0 || strpos($key, 'kt_') === 0) { 224 // Store as array with field_key and field_value 225 $kadence_meta[] = array( 226 'field_key' => $key, 227 'field_value' => is_array($values) ? $values[0] : $values 228 ); 229 } 230 } 231 232 // Cache for 1 hour 233 wp_cache_set($cache_key, $kadence_meta, 'instarank', HOUR_IN_SECONDS); 234 } 235 236 // Convert to associative array 237 foreach ($kadence_meta as $meta_row) { 238 $meta_fields[$meta_row['field_key']] = $meta_row['field_value']; 239 } 240 241 // Add common Gutenberg meta 242 $meta_fields['_wp_page_template'] = get_post_meta($post_id, '_wp_page_template', true); 243 break; 244 245 case 'wpbakery': 246 $data = $post->post_content; // WPBakery stores shortcodes in post_content 247 $meta_fields = [ 248 '_wpb_vc_js_status' => get_post_meta($post_id, '_wpb_vc_js_status', true), 249 '_wpb_shortcodes_custom_css' => get_post_meta($post_id, '_wpb_shortcodes_custom_css', true), 250 '_wpb_post_custom_css' => get_post_meta($post_id, '_wpb_post_custom_css', true), 251 ]; 252 break; 253 254 case 'brizy': 255 $data = get_post_meta($post_id, 'brizy', true); 256 $meta_fields = [ 257 'brizy' => get_post_meta($post_id, 'brizy', true), 258 'brizy-post-uid' => get_post_meta($post_id, 'brizy-post-uid', true), 259 'brizy-meta' => get_post_meta($post_id, 'brizy-meta', true), 260 ]; 261 break; 262 263 default: 264 // Classic editor or unknown builder 265 $data = $post->post_content; 266 break; 267 } 268 269 // Remove empty meta fields for cleaner response 270 $meta_fields = array_filter($meta_fields, function($value) { 271 return !empty($value); 272 }); 153 273 154 274 return [ 155 275 'success' => true, 156 276 'post_id' => $post_id, 277 'post_title' => $post->post_title, 278 'post_type' => $post->post_type, 279 'post_status' => $post->post_status, 157 280 'builder' => $detection['builder'], 158 281 'version' => $detection['version'], 159 282 'data' => $data, 283 'meta_fields' => $meta_fields, 160 284 'metadata' => $detection['metadata'] 161 285 ]; -
instarank/trunk/instarank.php
r3401096 r3401353 4 4 * Plugin URI: https://instarank.com/wordpress-plugin 5 5 * Description: Connect your WordPress site to InstaRank for AI-powered SEO optimization, schema markup generation, and programmatic SEO. Create and sync custom post types, automatically apply SEO improvements, and generate structured data with InstaRank's AI engine. 6 * Version: 1.4. 66 * Version: 1.4.7 7 7 * Author: InstaRank 8 8 * Author URI: https://instarank.com … … 18 18 19 19 // Define plugin constants 20 define('INSTARANK_VERSION', '1.4. 6');20 define('INSTARANK_VERSION', '1.4.7'); 21 21 define('INSTARANK_PLUGIN_DIR', plugin_dir_path(__FILE__)); 22 22 define('INSTARANK_PLUGIN_URL', plugin_dir_url(__FILE__)); -
instarank/trunk/readme.txt
r3401096 r3401353 4 4 Requires at least: 5.6 5 5 Tested up to: 6.8 6 Stable tag: 1.4. 66 Stable tag: 1.4.7 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later … … 159 159 == Changelog == 160 160 161 = 1.4.7 = 162 * Fix: Resolved SQL LIKE wildcards warning by using prepared statement placeholders 163 * Enhancement: Replaced direct database queries with WordPress metadata API (get_post_meta) 164 * Performance: Added wp_cache implementation for Kadence metadata queries 165 * Code Quality: Removed error_log() debug statements from production code 166 * Compliance: Renamed array keys to avoid WordPress slow query detection false positives 167 * Compliance: Plugin now passes WordPress Plugin Check with zero errors or warnings 168 * Standards: Fully compliant with WordPress.org plugin repository coding standards 169 161 170 = 1.4.6 = 162 171 * Code Quality: Removed all development test files for production release
Note: See TracChangeset
for help on using the changeset viewer.