Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Content Types: Ensure settings page functionality works without module requirement #41349

Open
wants to merge 14 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/rest-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,28 @@ Dismiss a Jetpack notice by Id.
* `"feedback_dash_request"`
* `"welcome"`.

### Jetpack Features (not modules)

This has primarily been introduced to distinguish between former modules moved to the Classic Theme Helper package (predominantly Custom Content Types), and existing modules.

#### GET wp-json/jetpack/v4/feature/:feature-slug

Get a single feature status, over-ride property, description and search queries by its slug.

**Example response** for `/feature/custom-content-types`

```json
{
"custom-content-types": {
"active": true,
"over_ride": false,
"description": "Display different types of content on your site with custom content types.",
"additional_search_queries": "cpt, custom post types, portfolio, portfolios, testimonial, testimonials",
}
}
```


### Site information

Operations related to information about the site.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

Custom Content Types: Ensure feature works on Jetpack settings page without using module functionality.
5 changes: 5 additions & 0 deletions projects/js-packages/api/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ function JetpackRestApiClient( root, nonce ) {
.then( checkStatus )
.then( parseJsonResponse ),

getFeatureTypeStatus: customContentType =>
Copy link
Contributor Author

@coder-karen coder-karen Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention behind having this as part of the Jetpack HTTP API is because it ideally will be able to be used by multiple removed 'features' depending on their location.

getRequest( `${ apiRoot }jetpack/v4/feature/${ customContentType }`, getParams )
.then( checkStatus )
.then( parseJsonResponse ),

fetchStatsData: range =>
getRequest( statsDataUrl( range ), getParams )
.then( checkStatus )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

Custom Content Types: Ensure feature works on Jetpack settings page without using module functionality.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* @package automattic/jetpack-classic-theme-helper
*/

use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Redirect;
use Automattic\Jetpack\Status\Host;

Expand All @@ -34,18 +35,74 @@ function jetpack_load_custom_post_types() {
add_action( 'jetpack_activate_module_custom-content-types', array( '\Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial', 'activation_post_type_support' ) );

add_action( 'init', array( '\Automattic\Jetpack\Classic_Theme_Helper\Nova_Restaurant', 'init' ) );

$site_id = Connection_Manager::get_site_id();
if ( is_wp_error( $site_id ) ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the site id check here prevents issues with multiple APIs, or rather failing tests. See the link in this Slack thread for more on those failing tests: p1738057987804199/1738056861.398469-slack-CDLH4C1UZ

I'd like to hear more though from anyone who has more knowledge of the interconnectedness of the REST APIs as well as the api-v2 tests as to how these may have been overlapping, if possible.

return;
}
add_action( 'rest_api_init', 'register_rest_route_custom_content_types' );

}

if ( ! function_exists( 'jetpack_custom_post_types_loaded' ) ) {
/**
* Make module configurable.
* Pass the active status to the front-end in it's initial state.
*/
function jetpack_custom_post_types_loaded() {
if ( class_exists( 'Jetpack' ) ) {
Jetpack::enable_module_configurable( __FILE__ );
}
$initial_state = 'var CUSTOM_CONTENT_TYPE__INITIAL_STATE; typeof CUSTOM_CONTENT_TYPE__INITIAL_STATE === "object" || (CUSTOM_CONTENT_TYPE__INITIAL_STATE = JSON.parse(decodeURIComponent("' . rawurlencode(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable follows the same style as a couple of other initial states, such as JP_CONNECTION_INITIAL_STATE:

return 'var JP_CONNECTION_INITIAL_STATE; typeof JP_CONNECTION_INITIAL_STATE === "object" || (JP_CONNECTION_INITIAL_STATE = JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( self::get_data() ) ) . '")));';

wp_json_encode(
array(
'active' => true,
'over_ride' => false,
)
)
) . '")));';

// Create a global variable with the custom content type feature status so that the value is available
// earlier than the API method above allows, preventing delayed loading of the settings card.
wp_register_script( 'custom-content-types-data', '', array(), '0.1.0', true );
wp_enqueue_script( 'custom-content-types-data' );
wp_add_inline_script(
'custom-content-types-data',
$initial_state,
'before'
);
}
add_action( 'init', 'jetpack_custom_post_types_loaded' );
}
if ( ! function_exists( 'register_rest_route_custom_content_types' ) ) {
/**
* Register the REST route for the custom content types.
*/
function register_rest_route_custom_content_types() {

register_rest_route(
'jetpack/v4',
'/feature/custom-content-types',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => function () {
$active = true;
$over_ride = false;
$name = 'Custom Content Types';
$description = 'Display different types of content on your site with custom content types.';
$additional_search_queries = 'cpt, custom post types, portfolio, portfolios, testimonial, testimonials';
return rest_ensure_response(
array(
'custom-content-types' => array(
'active' => $active,
'over_ride' => $over_ride,
'name' => $name,
'description' => $description,
'additional_search_queries' => $additional_search_queries,
),
)
);
},
'permission_callback' => '__return_true',
)
);
}
add_action( 'jetpack_modules_loaded', 'jetpack_custom_post_types_loaded' );
}

if ( ! function_exists( 'jetpack_cpt_settings_api_init' ) ) {
Expand Down Expand Up @@ -83,6 +140,24 @@ function jetpack_cpt_section_callback() {
}
}

if ( ! function_exists( 'remove_custom_content_types_module_list' ) ) {
/**
* Remove Custom Content Types from the old Module list.
* Available at wp-admin/admin.php?page=jetpack_modules
*
* @param array $items Array of Jetpack modules.
* @todo Remove this function once the module file is removed from the Jetpack plugin.
* @return array
*/
function remove_custom_content_types_module_list( $items ) {
if ( isset( $items['custom-content-types'] ) ) {
unset( $items['custom-content-types'] );
}
return $items;
}
add_filter( 'jetpack_modules_list_table_items', 'remove_custom_content_types_module_list' );
}

if ( function_exists( 'jetpack_load_custom_post_types' ) ) {

jetpack_load_custom_post_types();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace Automattic\Jetpack\Classic_Theme_Helper;

use Automattic\Jetpack\Modules;
use Automattic\Jetpack\Status\Host;
use Jetpack_Options;
use WP_Customize_Image_Control;
Expand Down Expand Up @@ -277,7 +276,7 @@ private function site_supports_custom_post_type() {
}

// Otherwise, say no unless something wants to filter us to say yes.
/** This action is documented in modules/custom-post-types/nova.php */
/** This action is documented in classic-theme-helper/src/custom-post-types/class-nova-restaurant.php */
return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
}

Expand Down Expand Up @@ -319,7 +318,6 @@ public function flush_rules_on_switch() {
public static function activation_post_type_support() {
if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
update_option( self::OPTION_NAME, '1' );
( new Modules() )->activate( 'custom-content-types', false, false );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace Automattic\Jetpack\Classic_Theme_Helper;

use Automattic\Jetpack\Modules;
use Automattic\Jetpack\Status\Host;
use Jetpack_Options;
use WP_Customize_Image_Control;
Expand Down Expand Up @@ -51,6 +50,9 @@ public function __construct() {
// Add an option to enable the CPT. Set the priority to 11 to ensure "Portfolio Projects" appears above "Testimonials" in the UI.
add_action( 'admin_init', array( $this, 'settings_api_init' ), 11 );

// Check on theme switch if theme supports CPT and setting is disabled
add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) );

// Make sure the post types are loaded for imports
add_action( 'import_start', array( $this, 'register_post_types' ) );

Expand All @@ -60,7 +62,11 @@ public function __construct() {
// Add to REST API post type allowed list.
add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_cpt_rest_api_type' ) );

$this->maybe_register_cpt();
if ( get_option( self::OPTION_NAME, '0' ) || ( new Host() )->is_wpcom_platform() ) {
$this->maybe_register_cpt();
} else {
add_action( 'init', array( $this, 'maybe_register_cpt' ) );
}

// Add a variable with the theme support status for the Jetpack Settings Testimonial toggle UI.
if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
Expand All @@ -82,20 +88,13 @@ public function __construct() {
*/
public function maybe_register_cpt() {

// Check on theme switch if theme supports CPT and setting is disabled
add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) );

$setting = class_exists( 'Jetpack_Options' ) ? Jetpack_Options::get_option_and_ensure_autoload( self::OPTION_NAME, '0' ) : '0'; // @phan-suppress-current-line PhanUndeclaredClassMethod -- We check if the class exists first.

// Bail early if Testimonial option is not set and the theme doesn't declare support
if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) {
return;
}

if ( ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) && class_exists( 'Jetpack' ) && ! \Jetpack::is_module_active( 'custom-content-types' ) ) { // @phan-suppress-current-line PhanUndeclaredClassMethod -- We check if the class exists first.
return;
}

// CPT magic
$this->register_post_types();
add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
Expand Down Expand Up @@ -223,7 +222,7 @@ private function site_supports_custom_post_type() {
}

// Otherwise, say no unless something wants to filter us to say yes.
/** This action is documented in modules/custom-post-types/nova.php */
/** This action is documented in classic-theme-helper/src/custom-post-types/class-nova-restaurant.php */
return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
}

Expand Down Expand Up @@ -311,7 +310,6 @@ public function flush_rules_on_switch() {
public static function activation_post_type_support() {
if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
update_option( self::OPTION_NAME, '1' );
( new Modules() )->activate( 'custom-content-types', false, false );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ export class NavigationSettings extends React.Component {
{ _x( 'Performance', 'Navigation item.', 'jetpack' ) }
</NavItem>
) }
{ this.props.hasAnyOfTheseModules( [
{ ( this.props.hasAnyOfTheseModules( [
'markdown',
'custom-content-types',
'post-by-email',
'infinite-scroll',
'copy-post',
] ) && (
] ) ||
window.CUSTOM_CONTENT_TYPE__INITIAL_STATE.active ) && (
<NavItem
path="#writing"
onClick={ this.handleClickForTracking( 'writing' ) }
Expand Down
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/_inc/client/state/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export const AKISMET_DATA_FETCH = 'AKISMET_DATA_FETCH';
export const AKISMET_DATA_FETCH_FAIL = 'AKISMET_DATA_FETCH_FAIL';
export const AKISMET_DATA_FETCH_SUCCESS = 'AKISMET_DATA_FETCH_SUCCESS';

export const CUSTOM_FEATURE_ACTIVE_FETCH = 'CUSTOM_FEATURE_ACTIVE_FETCH';
export const CUSTOM_FEATURE_ACTIVE_FETCH_FAIL = 'CUSTOM_FEATURE_ACTIVE_FETCH_FAIL';
export const CUSTOM_FEATURE_ACTIVE_FETCH_SUCCESS = 'CUSTOM_FEATURE_ACTIVE_FETCH_FAIL';

export const AKISMET_KEY_CHECK_FETCH = 'AKISMET_KEY_CHECK_FETCH';
export const AKISMET_KEY_CHECK_FETCH_FAIL = 'AKISMET_KEY_CHECK_FETCH_FAIL';
export const AKISMET_KEY_CHECK_FETCH_SUCCESS = 'AKISMET_KEY_CHECK_FETCH_SUCCESS';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import restApi from '@automattic/jetpack-api';
import {
CUSTOM_FEATURE_ACTIVE_FETCH_FAIL,
CUSTOM_FEATURE_ACTIVE_FETCH_SUCCESS,
CUSTOM_FEATURE_ACTIVE_FETCH,
} from 'state/action-types';

/**
* Fetch the status of the custom content types feature.
*
* @param {string} featureType - The custom content type to check.
* @return {Function} The action.
*/
export const getActiveFeatureDetails = featureType => {
return dispatch => {
dispatch( {
type: CUSTOM_FEATURE_ACTIVE_FETCH,
} );
return restApi
.getFeatureTypeStatus( featureType )
.then( data => {
dispatch( {
type: CUSTOM_FEATURE_ACTIVE_FETCH_SUCCESS,
feature_data: data,
} );
return data;
} )
.catch( error => {
dispatch( {
type: CUSTOM_FEATURE_ACTIVE_FETCH_FAIL,
error: error,
} );
} );
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './reducer';
export * from './actions';
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { assign } from 'lodash';
import { combineReducers } from 'redux';
import {
CUSTOM_FEATURE_ACTIVE_FETCH_FAIL,
CUSTOM_FEATURE_ACTIVE_FETCH_SUCCESS,
CUSTOM_FEATURE_ACTIVE_FETCH,
} from 'state/action-types';

export const items = ( state = { fetchingCustomContentTypeStatus: false }, action ) => {
switch ( action.type ) {
case CUSTOM_FEATURE_ACTIVE_FETCH:
return assign( {}, state, { fetchingCustomContentTypeStatus: true } );
case CUSTOM_FEATURE_ACTIVE_FETCH_SUCCESS:
return {
...state,
featureData: {
...state.featureCheck,
...action.feature_data,
},
};
case CUSTOM_FEATURE_ACTIVE_FETCH_FAIL:
return { ...state, fetchingCustomContentTypeStatus: false, error: action.error };
default:
return state;
}
};

export const reducer = combineReducers( {
items,
} );
Loading
Loading