<?php
namespace Classifai\Admin;
use Classifai\Features\Classification;
use Classifai\Services\ServicesManager;
use Classifai\Taxonomy\TaxonomyFactory;
use function Classifai\get_asset_info;
use function Classifai\get_plugin;
use function Classifai\get_post_types_for_language_settings;
use function Classifai\get_services_menu;
use function Classifai\get_post_statuses_for_language_settings;
use function Classifai\is_elasticpress_installed;
class Settings {
/**
* Register the actions needed.
*/
public function __construct() {}
/**
* Inintialize the class and register the actions needed.
*/
public function init() {
add_action( 'admin_menu', [ $this, 'register_settings_page' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
add_action( 'rest_api_init', [ $this, 'register_routes' ] );
}
/**
* Registers a hidden sub menu page for the onboarding wizard.
*/
public function register_settings_page() {
$registration_settings = get_option( 'classifai_settings' );
$page_title = esc_attr__( 'ClassifAI', 'classifai' );
$menu_title = $page_title;
if ( ! isset( $registration_settings['valid_license'] ) || ! $registration_settings['valid_license'] ) {
/*
* Translators: Menu title.
*/
$menu_title = sprintf( __( 'ClassifAI %s', 'classifai' ), '<span class="update-plugins"><span class="update-count">!</span></span>' );
}
add_submenu_page(
'tools.php',
$page_title,
$menu_title,
'manage_options',
'classifai',
[ $this, 'render_settings_page' ]
);
}
/**
* Renders the ClassifAI settings page.
*/
public function render_settings_page() {
?>
<div style="display: none;">
<hr class="wp-header-end" />
</div>
<div class="classifai-content classifai-settings" id="classifai-settings"></div>
<?php
}
/**
* Enqueue the scripts and styles needed for the settings page.
*
* @param string $hook_suffix The current admin page.
*/
public function admin_enqueue_scripts( $hook_suffix ) {
if ( ! in_array( $hook_suffix, array( 'tools_page_classifai' ), true ) ) {
return;
}
wp_enqueue_script(
'classifai-settings',
CLASSIFAI_PLUGIN_URL . 'dist/settings.js',
get_asset_info( 'settings', 'dependencies' ),
get_asset_info( 'settings', 'version' ),
true
);
wp_set_script_translations( 'classifai-settings', 'classifai' );
$post_types = get_post_types_for_language_settings();
$excerpt_post_types = array();
$post_type_options = array();
foreach ( $post_types as $post_type ) {
$post_type_options[ $post_type->name ] = $post_type->label;
if ( post_type_supports( $post_type->name, 'excerpt' ) ) {
$excerpt_post_types[ $post_type->name ] = $post_type->label;
}
}
$data = array(
'features' => $this->get_features(),
'services' => get_services_menu(),
'settings' => $this->get_settings(),
'dashboardUrl' => admin_url( '/' ),
'nonce' => wp_create_nonce( 'classifai-previewer-action' ),
'postTypes' => $post_type_options,
'excerptPostTypes' => $excerpt_post_types,
'postStatuses' => get_post_statuses_for_language_settings(),
'isEPinstalled' => is_elasticpress_installed(),
'nluTaxonomies' => $this->get_nlu_taxonomies(),
);
wp_add_inline_script(
'classifai-settings',
sprintf(
'var classifAISettings = %s;',
wp_json_encode( $data )
),
'before'
);
wp_enqueue_style(
'classifai-settings',
CLASSIFAI_PLUGIN_URL . 'dist/settings.css',
array( 'wp-edit-blocks' ),
get_asset_info( 'settings', 'version' ),
'all'
);
}
/**
* Get features for the settings page.
*
* @param bool $with_instance Whether to include the instance of the feature.
* @return array
*/
public function get_features( bool $with_instance = false ): array {
$services = get_plugin()->services;
if ( empty( $services ) || empty( $services['service_manager'] ) || ! $services['service_manager'] instanceof ServicesManager ) {
return [];
}
/** @var ServicesManager $service_manager Instance of the services manager class. */
$service_manager = $services['service_manager'];
$services = [];
if ( empty( $service_manager->service_classes ) ) {
return [];
}
if ( $with_instance ) {
foreach ( $service_manager->service_classes as $service ) {
foreach ( $service->feature_classes as $feature ) {
$services[ $feature::ID ] = $feature;
}
}
return $services;
}
$post_types = get_post_types_for_language_settings();
foreach ( $service_manager->service_classes as $service ) {
$services[ $service->get_menu_slug() ] = array();
foreach ( $service->feature_classes as $feature ) {
$services[ $service->get_menu_slug() ][ $feature::ID ] = array(
'label' => $feature->get_label(),
'providers' => $feature->get_providers(),
'roles' => $feature->get_roles(),
'enable_description' => $feature->get_enable_description(),
'taxonomies' => $feature->get_taxonomies(),
);
// Add taxonomies under post types for language processing features to allow filtering by post type.
if ( 'language_processing' === $service->get_menu_slug() && ! empty( $post_types ) ) {
$post_types_taxonomies = array();
foreach ( $post_types as $post_type ) {
$post_types_taxonomies[ $post_type->name ] = $feature->get_taxonomies( [ $post_type->name ] );
}
$services[ $service->get_menu_slug() ][ $feature::ID ]['taxonomiesByPostTypes'] = $post_types_taxonomies;
}
}
}
return $services;
}
/**
* Return the list of NLU taxonomies for the Classification feature settings.
*
* @return array
*/
public function get_nlu_taxonomies(): array {
$taxonomies = [];
$taxonomy_factory = new TaxonomyFactory();
$nlu_taxonomies = $taxonomy_factory->get_supported_taxonomies();
foreach ( $nlu_taxonomies as $taxonomy ) {
$taxonomy_instance = $taxonomy_factory->build( $taxonomy );
if ( ! $taxonomy_instance ) {
continue;
}
$taxonomies[ $taxonomy_instance->get_name() ] = $taxonomy_instance->get_singular_label();
}
/**
* Filter IBM Watson NLU taxonomies shown in settings.
*
* @since x.x.x
* @hook classifai_settings_ibm_watson_nlu_taxonomies
*
* @param {array} $taxonomies Array of IBM Watson NLU taxonomies.
*
* @return {array} Array of taxonomies.
*/
return apply_filters( 'classifai_settings_ibm_watson_nlu_taxonomies', $taxonomies );
}
/**
* Get the settings.
*
* @return array The settings.
*/
public function get_settings(): array {
$features = $this->get_features( true );
$settings = [];
foreach ( $features as $feature ) {
$settings[ $feature::ID ] = $feature->get_settings();
}
return $settings;
}
/**
* Register the REST API routes for the settings.
*/
public function register_routes() {
register_rest_route(
'classifai/v1',
'settings',
[
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_settings_callback' ],
'permission_callback' => [ $this, 'get_settings_permissions_check' ],
],
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ $this, 'update_settings_callback' ],
'permission_callback' => [ $this, 'update_settings_permissions_check' ],
],
]
);
register_rest_route(
'classifai/v1',
'registration',
[
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_registration_settings_callback' ],
'permission_callback' => [ $this, 'registration_settings_permissions_check' ],
],
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ $this, 'update_registration_settings_callback' ],
'permission_callback' => [ $this, 'registration_settings_permissions_check' ],
],
]
);
register_rest_route(
'classifai/v1',
'embeddings_in_progress',
[
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'check_embedding_generation_status' ],
'permission_callback' => [ $this, 'get_settings_permissions_check' ],
],
]
);
}
/**
* Callback for getting the settings.
*
* @return \WP_REST_Response
*/
public function get_settings_callback(): \WP_REST_Response {
$settings = $this->get_settings();
return rest_ensure_response( $settings );
}
/**
* Check if a given request has access to get settings.
*
* @return bool
*/
public function get_settings_permissions_check(): bool {
return current_user_can( 'manage_options' );
}
/**
* Update the settings.
*
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response|\WP_Error
*/
public function update_settings_callback( \WP_REST_Request $request ) {
$params = $request->get_json_params();
$settings = $params['settings'] ?? [];
$is_setup = $params['is_setup'] ?? false;
$step = $params['step'] ?? '';
$features = $this->get_features( true );
// Load settings error functions.
if ( ! function_exists( 'add_settings_error' ) ) {
require_once ABSPATH . 'wp-admin/includes/template.php';
}
foreach ( $settings as $feature_key => $feature_settings ) {
$feature = $features[ $feature_key ];
if ( ! $feature ) {
return new \WP_Error( 'invalid_feature', __( 'Invalid feature.', 'classifai' ), [ 'status' => 400 ] );
}
// Skip sanitizing settings for setup step 1.
if ( true === $is_setup && 'enable_features' === $step ) {
$current_settings = $feature->get_settings();
// Update only status of the feature.
$current_settings['status'] = sanitize_text_field( $feature_settings['status'] ?? $current_settings['status'] );
$new_settings = $current_settings;
} else {
$new_settings = $feature->sanitize_settings( $settings[ $feature_key ] );
if ( is_wp_error( $new_settings ) ) {
continue;
}
}
// Update settings.
$feature->update_settings( $new_settings );
$setting_errors = get_settings_errors();
$errors = array();
if ( ! empty( $setting_errors ) ) {
foreach ( $setting_errors as $setting_error ) {
if ( empty( $setting_error['message'] ) ) {
continue;
}
$errors[] = array(
'code' => $setting_error['code'],
'message' => wp_strip_all_tags( $setting_error['message'] ),
);
}
}
}
$response = array(
'success' => true,
'settings' => $this->get_settings(),
);
if ( ! empty( $errors ) ) {
$response['success'] = false;
$response['errors'] = $errors;
}
return rest_ensure_response( $response );
}
/**
* Check if a given request has access to update settings.
*
* @return bool
*/
public function update_settings_permissions_check(): bool {
return current_user_can( 'manage_options' );
}
/**
* Callback for getting the registration settings.
*
* @return \WP_REST_Response
*/
public function get_registration_settings_callback(): \WP_REST_Response {
$service_manager = new ServicesManager();
$settings = $service_manager->get_settings();
return rest_ensure_response( $settings );
}
/**
* Update the registration settings.
*
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response|\WP_Error
*/
public function update_registration_settings_callback( \WP_REST_Request $request ) {
// Load settings error functions.
if ( ! function_exists( 'add_settings_error' ) ) {
require_once ABSPATH . 'wp-admin/includes/template.php';
}
$service_manager = new ServicesManager();
$settings = $service_manager->get_settings();
$new_settings = $service_manager->sanitize_settings( $request->get_json_params() );
// Update the settings with the new values.
$new_settings = array_merge( $settings, $new_settings );
update_option( 'classifai_settings', $new_settings );
$setting_errors = get_settings_errors();
$errors = array();
if ( ! empty( $setting_errors ) ) {
foreach ( $setting_errors as $setting_error ) {
if ( empty( $setting_error['message'] ) ) {
continue;
}
$errors[] = array(
'code' => $setting_error['code'],
'message' => wp_strip_all_tags( $setting_error['message'] ),
);
}
}
$response = array(
'success' => true,
'settings' => $new_settings,
);
if ( ! empty( $errors ) ) {
$response['success'] = false;
$response['errors'] = $errors;
}
return rest_ensure_response( $response );
}
/**
* Check if a given request has access to get/update registration settings.
*
* @return bool
*/
public function registration_settings_permissions_check(): bool {
return current_user_can( 'manage_options' );
}
/**
* Callback for getting the registration settings.
*
* @return \WP_REST_Response
*/
public function check_embedding_generation_status(): \WP_REST_Response {
$classification = new Classification();
$response = array(
'classifAIEmbedInProgress' => $classification->is_embeddings_generation_in_progress(),
);
return rest_ensure_response( $response );
}
}