| @@ -14,8 +14,12 @@ | ||
| 14 | 14 | use Automattic\Jetpack\Blaze; |
| 15 | 15 | use Automattic\Jetpack\Status; |
| 16 | 16 | use Automattic\Jetpack\Status\Host; |
| 17 | 17 | |
| 18 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 19 | + exit( 0 ); | |
| 20 | +} | |
| 21 | + | |
| 18 | 22 | require_once __DIR__ . '/class.json-api-date.php'; |
| 19 | 23 | require_once __DIR__ . '/class.json-api-post-base.php'; |
| 20 | 24 | |
| 21 | 25 | /** |
| @@ -60,14 +64,24 @@ | ||
| 60 | 64 | return $this->blog_id; |
| 61 | 65 | } |
| 62 | 66 | |
| 63 | 67 | /** |
| 68 | + * Returns the site slug. | |
| 69 | + * | |
| 70 | + * @return string | |
| 71 | + */ | |
| 72 | + public function get_slug() { | |
| 73 | + return ( new Status() )->get_site_suffix(); | |
| 74 | + } | |
| 75 | + | |
| 76 | + /** | |
| 64 | 77 | * Returns the site name. |
| 65 | 78 | * |
| 66 | 79 | * @return string |
| 67 | 80 | */ |
| 68 | 81 | public function get_name() { |
| 69 | - return (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); | |
| 82 | + $name = get_bloginfo( 'name' ); | |
| 83 | + return is_string( $name ) ? htmlspecialchars_decode( $name, ENT_QUOTES ) : ''; | |
| 70 | 84 | } |
| 71 | 85 | |
| 72 | 86 | /** |
| 73 | 87 | * Returns the site description. |
| @@ -74,9 +88,10 @@ | ||
| 74 | 88 | * |
| 75 | 89 | * @return string |
| 76 | 90 | */ |
| 77 | 91 | public function get_description() { |
| 78 | - return (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES ); | |
| 92 | + $description = get_bloginfo( 'description' ); | |
| 93 | + return is_string( $description ) ? htmlspecialchars_decode( $description, ENT_QUOTES ) : ''; | |
| 79 | 94 | } |
| 80 | 95 | |
| 81 | 96 | /** |
| 82 | 97 | * Returns the URL for the current site. |
| @@ -757,26 +772,29 @@ | ||
| 757 | 772 | private function user_can_view_post( $post ) { |
| 758 | 773 | if ( ! $post || is_wp_error( $post ) ) { |
| 759 | 774 | return false; |
| 760 | 775 | } |
| 761 | - | |
| 762 | - if ( 'inherit' === $post->post_status ) { | |
| 776 | + // If the post is of status inherit, check if the parent exists ( different to 0 ) to check for the parent status object. | |
| 777 | + if ( 'inherit' === $post->post_status && 0 !== (int) $post->post_parent ) { | |
| 763 | 778 | $parent_post = get_post( $post->post_parent ); |
| 764 | - $post_status_obj = get_post_status_object( $parent_post->post_status ); | |
| 779 | + $post_status_obj = $parent_post ? get_post_status_object( $parent_post->post_status ) : null; | |
| 765 | 780 | } else { |
| 766 | 781 | $post_status_obj = get_post_status_object( $post->post_status ); |
| 767 | 782 | } |
| 768 | 783 | |
| 769 | - $authorized = ( | |
| 770 | - $post_status_obj->public || | |
| 771 | - ( is_user_logged_in() && | |
| 772 | - ( | |
| 773 | - ( $post_status_obj->protected && current_user_can( 'edit_post', $post->ID ) ) || | |
| 774 | - ( $post_status_obj->private && current_user_can( 'read_post', $post->ID ) ) || | |
| 775 | - ( 'trash' === $post->post_status && current_user_can( 'edit_post', $post->ID ) ) || | |
| 776 | - 'auto-draft' === $post->post_status | |
| 777 | - ) | |
| 778 | - ) | |
| 784 | + $authorized = false; | |
| 785 | + | |
| 786 | + if ( $post_status_obj ) { | |
| 787 | + $authorized = $post_status_obj->public | |
| 788 | + || ( is_user_logged_in() && ( | |
| 789 | + ( $post_status_obj->protected && current_user_can( 'edit_post', $post->ID ) ) | |
| 790 | + || ( $post_status_obj->private && current_user_can( 'read_post', $post->ID ) ) | |
| 791 | + ) ); | |
| 792 | + } | |
| 793 | + | |
| 794 | + $authorized = $authorized || ( | |
| 795 | + ( 'trash' === $post->post_status && current_user_can( 'edit_post', $post->ID ) ) | |
| 796 | + || 'auto-draft' === $post->post_status | |
| 779 | 797 | ); |
| 780 | 798 | |
| 781 | 799 | if ( ! $authorized ) { |
| 782 | 800 | return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); |
| @@ -958,18 +976,8 @@ | ||
| 958 | 976 | 'upload_files' => $this->current_user_can( 'upload_files' ), |
| 959 | 977 | 'delete_users' => $this->current_user_can( 'delete_users' ), |
| 960 | 978 | 'remove_users' => $this->current_user_can( 'remove_users' ), |
| 961 | 979 | 'own_site' => $is_wpcom_blog_owner, |
| 962 | - /** | |
| 963 | - * Filter whether the Hosting section in Calypso should be available for site. | |
| 964 | - * | |
| 965 | - * @module json-api | |
| 966 | - * | |
| 967 | - * @since 8.2.0 | |
| 968 | - * | |
| 969 | - * @param bool $view_hosting Can site access Hosting section. Default to false. | |
| 970 | - */ | |
| 971 | - 'view_hosting' => apply_filters( 'jetpack_json_api_site_can_view_hosting', false ), | |
| 972 | 980 | 'view_stats' => stats_is_blog_user( $this->blog_id ), |
| 973 | 981 | 'activate_plugins' => $this->current_user_can( 'activate_plugins' ), |
| 974 | 982 | 'update_plugins' => $this->current_user_can( 'update_plugins' ), |
| 975 | 983 | 'export' => $this->current_user_can( 'export' ), |
| @@ -1006,9 +1014,9 @@ | ||
| 1006 | 1014 | **/ |
| 1007 | 1015 | public function get_logo() { |
| 1008 | 1016 | // Set an empty response array. |
| 1009 | 1017 | $logo_setting = array( |
| 1010 | - 'id' => (int) 0, | |
| 1018 | + 'id' => 0, | |
| 1011 | 1019 | 'sizes' => array(), |
| 1012 | 1020 | 'url' => '', |
| 1013 | 1021 | ); |
| 1014 | 1022 | |
| @@ -1247,9 +1255,9 @@ | ||
| 1247 | 1255 | $ss = new Sharing_Service(); |
| 1248 | 1256 | $blog_services = $ss->get_blog_services(); |
| 1249 | 1257 | $default_sharing_status = ! empty( $blog_services['visible'] ); |
| 1250 | 1258 | } |
| 1251 | - return (bool) $default_sharing_status; | |
| 1259 | + return $default_sharing_status; | |
| 1252 | 1260 | } |
| 1253 | 1261 | |
| 1254 | 1262 | /** |
| 1255 | 1263 | * Displays the current comment status |
| @@ -1511,8 +1519,68 @@ | ||
| 1511 | 1519 | return false; |
| 1512 | 1520 | } |
| 1513 | 1521 | |
| 1514 | 1522 | /** |
| 1523 | + * Get the DIFM Lite site options exposed to the frontend while a build is in progress. | |
| 1524 | + * | |
| 1525 | + * Returns null unless the site has an active DIFM Lite build and the code is | |
| 1526 | + * running on WordPress.com, where the DIFM Lite library exists. | |
| 1527 | + * | |
| 1528 | + * @return array|null | |
| 1529 | + */ | |
| 1530 | + public function get_difm_lite_site_options() { | |
| 1531 | + if ( ! $this->is_difm_lite_in_progress() ) { | |
| 1532 | + return null; | |
| 1533 | + } | |
| 1534 | + if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM || ! function_exists( 'require_lib' ) ) { | |
| 1535 | + return null; | |
| 1536 | + } | |
| 1537 | + require_lib( 'difm-lite' ); | |
| 1538 | + if ( ! class_exists( '\DIFM_Lite_Options' ) ) { | |
| 1539 | + // Fail closed if the library did not define the class; this method | |
| 1540 | + // documents null for every path where the options are unavailable. | |
| 1541 | + return null; | |
| 1542 | + } | |
| 1543 | + // The submission state is canonical on the purchase blog. This site may be | |
| 1544 | + // the stickered staging build target, whose own options blob is empty, so | |
| 1545 | + // resolve to the purchase blog before reading — otherwise a post-submit | |
| 1546 | + // staging target reports itself as pre-submit. | |
| 1547 | + // | |
| 1548 | + // resolve_purchase_site_id() ships in the wpcom DIFM Lite site-role | |
| 1549 | + // pointers change. This Jetpack code can deploy before that lands, so | |
| 1550 | + // fall back to the current blog when it is unavailable — no retargeted | |
| 1551 | + // builds can exist until that change is live, so the current blog is the | |
| 1552 | + // purchase blog in that window. | |
| 1553 | + $purchase_blog_id = $this->blog_id; | |
| 1554 | + // @phan-suppress-next-line PhanUndeclaredClassReference -- wpcom-only class, guarded above. | |
| 1555 | + if ( method_exists( '\DIFM_Lite_Options', 'resolve_purchase_site_id' ) ) { | |
| 1556 | + // @phan-suppress-next-line PhanUndeclaredClassMethod -- wpcom-only class, guarded above. | |
| 1557 | + $purchase_blog_id = \DIFM_Lite_Options::resolve_purchase_site_id( $this->blog_id ); | |
| 1558 | + } | |
| 1559 | + // @phan-suppress-next-line PhanUndeclaredClassMethod -- wpcom-only class, guarded above. | |
| 1560 | + $difm_lite_options = new \DIFM_Lite_Options( $purchase_blog_id ); | |
| 1561 | + return array( | |
| 1562 | + // @phan-suppress-next-line PhanUndeclaredClassProperty -- wpcom-only class, guarded above. | |
| 1563 | + 'is_website_content_submitted' => (bool) $difm_lite_options->is_website_content_submitted, | |
| 1564 | + ); | |
| 1565 | + } | |
| 1566 | + | |
| 1567 | + /** | |
| 1568 | + * Check if the site has the gating-business-q1 blog sticker. | |
| 1569 | + * | |
| 1570 | + * @return bool | |
| 1571 | + */ | |
| 1572 | + public function is_gating_business_q1() { | |
| 1573 | + if ( function_exists( 'has_blog_sticker' ) ) { | |
| 1574 | + return has_blog_sticker( 'gating-business-q1' ); | |
| 1575 | + } elseif ( function_exists( 'wpcomsh_is_site_sticker_active' ) ) { | |
| 1576 | + // For atomic sites | |
| 1577 | + return wpcomsh_is_site_sticker_active( 'gating-business-q1' ); | |
| 1578 | + } | |
| 1579 | + return false; | |
| 1580 | + } | |
| 1581 | + | |
| 1582 | + /** | |
| 1515 | 1583 | * Get the option of site intent which value is coming from the Hero Flow |
| 1516 | 1584 | * |
| 1517 | 1585 | * @return string |
| 1518 | 1586 | */ |
| @@ -1520,8 +1588,17 @@ | ||
| 1520 | 1588 | return get_option( 'site_intent', '' ); |
| 1521 | 1589 | } |
| 1522 | 1590 | |
| 1523 | 1591 | /** |
| 1592 | + * Get the option of site partner bundle which value is coming from the Partner Flow | |
| 1593 | + * | |
| 1594 | + * @return string | |
| 1595 | + */ | |
| 1596 | + public function get_site_partner_bundle() { | |
| 1597 | + return get_option( 'site_partner_bundle', '' ); | |
| 1598 | + } | |
| 1599 | + | |
| 1600 | + /** | |
| 1524 | 1601 | * Get site option to determine if and how to display launchpad onboarding |
| 1525 | 1602 | * |
| 1526 | 1603 | * @return string |
| 1527 | 1604 | */ |
| @@ -1553,8 +1630,56 @@ | ||
| 1553 | 1630 | return array(); |
| 1554 | 1631 | } |
| 1555 | 1632 | |
| 1556 | 1633 | /** |
| 1634 | + * Whether the AI Launchpad is enabled for this site, for the requesting user. | |
| 1635 | + * | |
| 1636 | + * The launchpad-personalization assignment is user-scoped: every site of an | |
| 1637 | + * ai_launchpad user gets the AI Launchpad, however the site was created. Mirrors | |
| 1638 | + * AI_Launchpad::is_enabled_for_site() in jetpack-mu-wpcom, so wp-admin and the | |
| 1639 | + * Calypso-facing payload agree. Like the capabilities in this payload, the value | |
| 1640 | + * is relative to the current user. | |
| 1641 | + * | |
| 1642 | + * @return bool | |
| 1643 | + */ | |
| 1644 | + public function is_ai_launchpad_enabled() { | |
| 1645 | + if ( (bool) get_option( 'wpcom_ai_launchpad_enabled' ) ) { | |
| 1646 | + return true; | |
| 1647 | + } | |
| 1648 | + | |
| 1649 | + return class_exists( '\Automattic\Jetpack\Jetpack_Mu_Wpcom\Launchpad_Personalization_Experiment' ) | |
| 1650 | + // @phan-suppress-next-line PhanUndeclaredClassMethod -- Lives in jetpack-mu-wpcom, outside this plugin's dependency graph; the class_exists guard above covers contexts where it isn't loaded. | |
| 1651 | + && 'ai_launchpad' === \Automattic\Jetpack\Jetpack_Mu_Wpcom\Launchpad_Personalization_Experiment::get_variation(); | |
| 1652 | + } | |
| 1653 | + | |
| 1654 | + /** | |
| 1655 | + * Whether the AI Launchpad was dismissed, reverting the site to the regular launchpad. | |
| 1656 | + * | |
| 1657 | + * @return bool | |
| 1658 | + */ | |
| 1659 | + public function is_ai_launchpad_dismissed() { | |
| 1660 | + return (bool) get_option( 'wpcom_ai_launchpad_dismissed' ); | |
| 1661 | + } | |
| 1662 | + | |
| 1663 | + /** | |
| 1664 | + * Whether every AI Launchpad task has been completed. | |
| 1665 | + * | |
| 1666 | + * @return bool | |
| 1667 | + */ | |
| 1668 | + public function is_ai_launchpad_completed() { | |
| 1669 | + return (bool) get_option( 'wpcom_ai_launchpad_completed' ); | |
| 1670 | + } | |
| 1671 | + | |
| 1672 | + /** | |
| 1673 | + * Get site option for migration source site domain | |
| 1674 | + * | |
| 1675 | + * @return string | |
| 1676 | + */ | |
| 1677 | + public function get_migration_source_site_domain() { | |
| 1678 | + return get_option( 'migration_source_site_domain', '' ); | |
| 1679 | + } | |
| 1680 | + | |
| 1681 | + /** | |
| 1557 | 1682 | * Detect whether a site is WordPress.com Staging Site. |
| 1558 | 1683 | * |
| 1559 | 1684 | * @see class.json-api-site-jetpack.php for implementation. |
| 1560 | 1685 | */ |
| @@ -1654,8 +1779,20 @@ | ||
| 1654 | 1779 | return ! empty( get_option( 'wpcom_classic_early_release' ) ); |
| 1655 | 1780 | } |
| 1656 | 1781 | |
| 1657 | 1782 | /** |
| 1783 | + * Returns whether APM (Application Performance Monitoring) is enabled for the site. | |
| 1784 | + * | |
| 1785 | + * APM is an Atomic-only hosting feature. Non-Atomic site types (Simple wpcom, real | |
| 1786 | + * Jetpack) return false; Jetpack_Shadow_Site overrides with the actual read. | |
| 1787 | + * | |
| 1788 | + * @return bool | |
| 1789 | + **/ | |
| 1790 | + public function get_apm_enabled() { | |
| 1791 | + return false; | |
| 1792 | + } | |
| 1793 | + | |
| 1794 | + /** | |
| 1658 | 1795 | * Get Zendesk site meta. |
| 1659 | 1796 | * |
| 1660 | 1797 | * @return array|null |
| 1661 | 1798 | */ |
| @@ -1666,5 +1803,82 @@ | ||
| 1666 | 1803 | * |
| 1667 | 1804 | * @return bool |
| 1668 | 1805 | */ |
| 1669 | 1806 | abstract public function is_pending_plan(); |
| 1807 | + | |
| 1808 | + /** | |
| 1809 | + * Detect whether the site is a Garden site. | |
| 1810 | + * | |
| 1811 | + * @return bool | |
| 1812 | + */ | |
| 1813 | + public function is_garden() { | |
| 1814 | + return false; | |
| 1815 | + } | |
| 1816 | + | |
| 1817 | + /** | |
| 1818 | + * Get the Garden name. | |
| 1819 | + * | |
| 1820 | + * @return string | |
| 1821 | + */ | |
| 1822 | + public function garden_name() { | |
| 1823 | + return null; | |
| 1824 | + } | |
| 1825 | + | |
| 1826 | + /** | |
| 1827 | + * Get the Garden partner. | |
| 1828 | + * | |
| 1829 | + * @return string | |
| 1830 | + */ | |
| 1831 | + public function garden_partner() { | |
| 1832 | + return null; | |
| 1833 | + } | |
| 1834 | + | |
| 1835 | + /** | |
| 1836 | + * Detect whether the Garden site is provisioned. | |
| 1837 | + * | |
| 1838 | + * @return bool|null | |
| 1839 | + */ | |
| 1840 | + public function garden_is_provisioned() { | |
| 1841 | + return null; | |
| 1842 | + } | |
| 1843 | + | |
| 1844 | + /** | |
| 1845 | + * Detect whether the site is a Flex site. | |
| 1846 | + * | |
| 1847 | + * @return bool | |
| 1848 | + */ | |
| 1849 | + public function is_wpcom_flex() { | |
| 1850 | + if ( function_exists( 'has_blog_sticker' ) ) { | |
| 1851 | + return has_blog_sticker( 'flex-cache-site' ); | |
| 1852 | + } | |
| 1853 | + return false; | |
| 1854 | + } | |
| 1855 | + | |
| 1856 | + /** | |
| 1857 | + * Detect whether Big Sky AI assistant is enabled for this site. | |
| 1858 | + * | |
| 1859 | + * @return bool | |
| 1860 | + */ | |
| 1861 | + public function is_big_sky_enabled() { | |
| 1862 | + return false; | |
| 1863 | + } | |
| 1864 | + | |
| 1865 | + /** | |
| 1866 | + * Get the state of any block on the site's outgoing email. | |
| 1867 | + * | |
| 1868 | + * @return array|null `status` (`blocked` or `at_risk`), `reason` and `expires_on`, | |
| 1869 | + * or null if the site has never been blocked. | |
| 1870 | + */ | |
| 1871 | + public function get_atomic_email_block() { | |
| 1872 | + return null; | |
| 1873 | + } | |
| 1874 | + | |
| 1875 | + /** | |
| 1876 | + * Get Jetpack recovery mode status. | |
| 1877 | + * | |
| 1878 | + * @return array|null | |
| 1879 | + */ | |
| 1880 | + public function get_jetpack_recovery_mode_status() { | |
| 1881 | + $status = get_option( 'jetpack_recovery_mode_status' ); | |
| 1882 | + return is_array( $status ) ? $status : null; | |
| 1883 | + } | |
| 1670 | 1884 | } |