Skip to content

Commit

Permalink
Custom Content Types: Ensure settings page functionality works withou…
Browse files Browse the repository at this point in the history
…t module requirement (#41349)
  • Loading branch information
coder-karen authored Feb 4, 2025
1 parent fdcb681 commit b1341be
Show file tree
Hide file tree
Showing 20 changed files with 358 additions and 117 deletions.
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 =>
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.
104 changes: 99 additions & 5 deletions projects/packages/classic-theme-helper/src/custom-content-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,96 @@ 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' ) );

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(
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' => 'get_custom_content_type_details',
'permission_callback' => 'custom_content_require_admin_privilege_callback',
)
);
}
}

/**
* Get the custom content type details.
*
* @return WP_REST_Response
*/
function get_custom_content_type_details() {

$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,
),
)
);
}

/**
* Check if the current user has the required capability.
*
* @return bool|WP_Error True if the request is made by ad administrator, WP_Error otherwise.
*/
function custom_content_require_admin_privilege_callback() {
if ( current_user_can( 'manage_options' ) ) {
return true;
}
add_action( 'jetpack_modules_loaded', 'jetpack_custom_post_types_loaded' );

return new WP_Error(
'rest_forbidden',
esc_html__( 'You are not allowed to perform this action.', 'jetpack-classic-theme-helper' ),
array( 'status' => rest_authorization_required_code() )
);
}

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

/**
* 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

0 comments on commit b1341be

Please sign in to comment.