post_type ) {
return;
}
echo '
' . esc_html__( 'Name the agent, choose a model, then copy the shortcode from the sidebar to put the chat on a page.', 'arwp-agents' ) . '
';
}
/**
* Prompt setup when the API key is missing.
*/
public function maybe_setup_notice() {
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
if ( ! $screen || 'edit-arwp_agent' !== $screen->id ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
if ( class_exists( 'ARWP_Settings' ) && ARWP_Settings::get_api_key() ) {
return;
}
echo '';
}
/**
* Warn once when blocked patterns were dropped on save.
*/
public function maybe_pattern_notice() {
$user_id = get_current_user_id();
if ( ! $user_id ) {
return;
}
$key = 'arwp_admin_notice_' . $user_id;
$text = get_transient( $key );
if ( ! is_string( $text ) || '' === $text ) {
return;
}
delete_transient( $key );
echo '' . esc_html( $text ) . '
';
}
/**
* Set default title for new agents.
*
* @param string $title Default title.
* @param WP_Post $post Post being created.
* @return string
*/
public function set_default_agent_title( $title, $post ) {
if ( 'arwp_agent' === $post->post_type ) {
return __( 'New AI Agent', 'arwp-agents' );
}
return $title;
}
/**
* Define custom columns for the arwp_agent post type list table.
*
* @param array $columns Existing columns.
* @return array
*/
public function set_custom_agent_columns( $columns ) {
$new_columns = array();
foreach ( $columns as $key => $title ) {
if ( 'date' === $key ) {
$new_columns['arwp_model'] = __( 'Model', 'arwp-agents' );
$new_columns['arwp_shortcode'] = __( 'Shortcode', 'arwp-agents' );
}
$new_columns[ $key ] = $title;
}
return $new_columns;
}
/**
* Render content for custom columns in the arwp_agent list table.
*
* @param string $column Column key.
* @param int $post_id Post ID.
*/
public function custom_agent_column_content( $column, $post_id ) {
if ( 'arwp_shortcode' === $column ) {
$shortcode = '[arwp id="' . intval( $post_id ) . '"]';
?>
' . esc_html__( 'Global Default', 'arwp-agents' ) . '';
} else {
$fetched_models = class_exists( 'ARWP_Settings' ) ? ARWP_Settings::get_models() : array();
$label = isset( $fetched_models[ $model ] ) ? $fetched_models[ $model ] : $model;
echo esc_html( $label );
}
}
}
/**
* Add Model filter dropdown to All Agents screen.
*
* @param string $post_type Current post type.
*/
public function add_agent_model_filter( $post_type ) {
if ( 'arwp_agent' !== $post_type ) {
return;
}
$fetched_models = class_exists( 'ARWP_Settings' ) ? ARWP_Settings::get_models() : array();
$models = array(
'default' => __( 'Global Default Model', 'arwp-agents' ),
) + $fetched_models;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only admin list filter, mirrors core's own GET-based list table filters.
$selected = isset( $_GET['arwp_model_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['arwp_model_filter'] ) ) : '';
echo '';
}
/**
* Filter agents by selected model.
*
* @param WP_Query $query Main query.
*/
public function filter_agents_by_model( $query ) {
global $pagenow;
// Read-only admin list filter, mirrors core's own GET-based list table filters (e.g. ?post_status=), so no nonce is required.
if ( is_admin() && 'edit.php' === $pagenow && isset( $_GET['post_type'] ) && 'arwp_agent' === $_GET['post_type'] && $query->is_main_query() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$filter_val = isset( $_GET['arwp_model_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['arwp_model_filter'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( '' !== $filter_val ) {
$meta_query = $query->get( 'meta_query' );
if ( ! is_array( $meta_query ) ) {
$meta_query = array();
}
if ( 'default' === $filter_val ) {
$meta_query[] = array(
'relation' => 'OR',
array(
'key' => '_arwp_model',
'value' => 'default',
'compare' => '=',
),
array(
'key' => '_arwp_model',
'compare' => 'NOT EXISTS',
),
);
} else {
$meta_query[] = array(
'key' => '_arwp_model',
'value' => $filter_val,
'compare' => '=',
);
}
$query->set( 'meta_query', $meta_query );
}
}
}
/**
* Add 'Clone' link to post row actions.
*
* @param array $actions Row actions.
* @param WP_Post $post Current post.
* @return array
*/
public function add_clone_agent_action( $actions, $post ) {
if ( 'arwp_agent' === $post->post_type ) {
$url = wp_nonce_url( admin_url( 'admin.php?action=arwp_clone_agent&post=' . $post->ID ), 'arwp_clone_agent_nonce', 'nonce' );
$actions['clone'] = '' . esc_html__( 'Clone', 'arwp-agents' ) . '';
}
return $actions;
}
/**
* Handle the clone agent action.
*/
public function handle_clone_agent() {
if ( ! isset( $_GET['action'] ) || 'arwp_clone_agent' !== $_GET['action'] ) {
return;
}
if ( ! isset( $_GET['post'] ) || ! isset( $_GET['nonce'] ) ) {
wp_die( esc_html__( 'Missing required parameters.', 'arwp-agents' ) );
}
$post_id = intval( $_GET['post'] );
if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'arwp_clone_agent_nonce' ) ) {
wp_die( esc_html__( 'Security check failed.', 'arwp-agents' ) );
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( esc_html__( 'You do not have permission to clone agents.', 'arwp-agents' ) );
}
$post = get_post( $post_id );
if ( ! $post || 'arwp_agent' !== $post->post_type ) {
wp_die( esc_html__( 'Invalid agent.', 'arwp-agents' ) );
}
// Create a copy of the post.
$new_post_args = array(
'post_title' => $post->post_title . ' ' . __( '(Copy)', 'arwp-agents' ),
'post_content' => $post->post_content,
'post_status' => 'draft', // Draft is safer so they don't get confused about which is which before publishing.
'post_type' => $post->post_type,
'post_author' => get_current_user_id(),
);
$new_post_id = wp_insert_post( $new_post_args );
if ( ! is_wp_error( $new_post_id ) ) {
$meta_data = get_post_custom( $post_id );
$skip_meta = array(
'_edit_lock',
'_edit_last',
);
foreach ( $meta_data as $key => $values ) {
if ( in_array( $key, $skip_meta, true ) ) {
continue;
}
foreach ( $values as $value ) {
$unserialized = maybe_unserialize( $value );
// add_post_meta() removes slashes. Put them back so \d in a pattern survives.
add_post_meta( $new_post_id, $key, wp_slash( $unserialized ) );
}
}
// Redirect back to the agent list.
wp_safe_redirect( admin_url( 'edit.php?post_type=arwp_agent' ) );
exit;
} else {
wp_die( esc_html__( 'Failed to clone the agent.', 'arwp-agents' ) );
}
}
/**
* Admin CSS and editor/list scripts for ARWP Agents screens.
*
* @param string $hook Current admin page hook.
*/
public function enqueue_admin_scripts( $hook ) {
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
$is_ours = ( false !== strpos( $hook, 'arwp-settings' ) || false !== strpos( $hook, 'arwp-conversations' ) );
if ( $screen && isset( $screen->post_type ) && 'arwp_agent' === $screen->post_type ) {
$is_ours = true;
}
if ( ! $is_ours ) {
return;
}
wp_register_style( 'arwp-admin', false, array(), ARWP_VERSION );
wp_enqueue_style( 'arwp-admin' );
wp_add_inline_style(
'arwp-admin',
'
.arwp-admin-help { margin: 8px 0 16px; color: #646970; font-size: 14px; }
.arwp-meta-table { width: 100%; border-spacing: 0 12px; }
.arwp-meta-table th { text-align: left; vertical-align: top; width: 200px; font-weight: 600; }
.arwp-meta-table td input[type="number"],
.arwp-meta-table td select,
.arwp-meta-table td textarea { width: 100%; max-width: 600px; box-sizing: border-box; }
.arwp-meta-table td textarea { min-height: 120px; }
.arwp-meta-table td p.description { margin-top: 4px; color: #666; font-size: 12px; }
.arwp-role-list label { display: block; margin: 4px 0; }
#arwp_access_roles_wrap { margin-top: 8px; }
.arwp-role-limits p { margin: 6px 0; }
.arwp-role-limits label { display: inline-block; min-width: 140px; }
.post-type-arwp_agent #misc-publishing-actions,
.post-type-arwp_agent #preview-action,
.post-type-arwp_agent #delete-action { display: none !important; }
.post-type-arwp_agent #submitpost { display: flex; justify-content: space-between; align-items: center; padding: 15px 10px; }
.post-type-arwp_agent #minor-publishing,
.post-type-arwp_agent #minor-publishing-actions,
.post-type-arwp_agent #major-publishing-actions,
.post-type-arwp_agent #publishing-action,
.post-type-arwp_agent #save-action { display: contents !important; }
'
);
$is_editor = ( 'post.php' === $hook || 'post-new.php' === $hook ) && $screen && 'arwp_agent' === $screen->post_type;
$is_list = ( 'edit.php' === $hook && $screen && 'edit-arwp_agent' === $screen->id );
$admin_deps = array( 'jquery' );
if ( $is_editor ) {
wp_enqueue_media();
$admin_deps[] = 'media-editor';
}
wp_register_script( 'arwp-admin', false, $admin_deps, ARWP_VERSION, true );
if ( $is_editor ) {
wp_enqueue_style( 'arwp-select2-css', ARWP_URL . 'assets/vendor/select2/select2.min.css', array(), '4.1.0' );
wp_enqueue_script( 'arwp-select2-js', ARWP_URL . 'assets/vendor/select2/select2.min.js', array( 'jquery' ), '4.1.0', true );
wp_enqueue_script( 'arwp-admin' );
wp_localize_script(
'arwp-admin',
'arwp_agent_editor',
array(
'select_logo' => __( 'Select header logo', 'arwp-agents' ),
'use_logo' => __( 'Use this logo', 'arwp-agents' ),
)
);
wp_add_inline_script( 'arwp-admin', $this->editor_script() );
}
if ( $is_list ) {
wp_enqueue_script( 'arwp-admin' );
wp_add_inline_script(
'arwp-admin',
"jQuery(function($){
$('.arwp-list-shortcode-input').on('click', function() {
$(this).select();
document.execCommand('copy');
$(this).next('.arwp-list-copied-msg').stop().fadeIn('fast').delay(1500).fadeOut('fast');
});
});"
);
}
}
/**
* Agent editor behavior: model picker, access roles, and header logo.
*
* @return string
*/
private function editor_script() {
return <<<'JS'
jQuery(function($){
$('#arwp_model').select2({ width: '100%', maxWidth: '600px' });
$('#arwp_shortcode_input').on('click', function() {
this.select();
document.execCommand('copy');
$('#arwp_shortcode_msg').stop().fadeIn().delay(2000).fadeOut();
});
var accessSelect = $('#arwp_access');
var rolesWrap = $('#arwp_access_roles_wrap');
var syncAccess = function() {
if (accessSelect.val() === 'roles') {
rolesWrap.show();
} else {
rolesWrap.hide();
}
};
accessSelect.on('change', syncAccess);
syncAccess();
var editor = window.arwp_agent_editor || {};
var logoFrame;
$('#arwp_ui_logo_select').on('click', function(e) {
e.preventDefault();
if (typeof wp === 'undefined' || !wp.media) {
return;
}
if (logoFrame) {
logoFrame.open();
return;
}
logoFrame = wp.media({
title: editor.select_logo || 'Select header logo',
button: { text: editor.use_logo || 'Use this logo' },
library: { type: 'image' },
multiple: false
});
logoFrame.on('select', function() {
var attachment = logoFrame.state().get('selection').first().toJSON();
var url = attachment.url;
if (attachment.sizes && attachment.sizes.medium && attachment.sizes.medium.url) {
url = attachment.sizes.medium.url;
}
$('#arwp_ui_logo_id').val(attachment.id);
$('#arwp_ui_logo_preview').empty().append($('
', { src: url, alt: '' }).css({ maxHeight: '48px', width: 'auto' }));
$('#arwp_ui_logo_remove').show();
});
logoFrame.open();
});
$('#arwp_ui_logo_remove').on('click', function(e) {
e.preventDefault();
$('#arwp_ui_logo_id').val('0');
$('#arwp_ui_logo_preview').empty();
$(this).hide();
});
});
JS;
}
/**
* Register arwp_agent Custom Post Type.
*/
public function register_arwp_agent_cpt() {
$labels = array(
'name' => _x( 'Agents', 'Post Type General Name', 'arwp-agents' ),
'singular_name' => _x( 'Agent', 'Post Type Singular Name', 'arwp-agents' ),
'menu_name' => __( 'ARWP Agents', 'arwp-agents' ),
'name_admin_bar' => __( 'Agent', 'arwp-agents' ),
'archives' => __( 'Agent Archives', 'arwp-agents' ),
'attributes' => __( 'Agent Attributes', 'arwp-agents' ),
'parent_item_colon' => __( 'Parent Agent:', 'arwp-agents' ),
'all_items' => __( 'Agents', 'arwp-agents' ),
'add_new_item' => __( 'Add New Agent', 'arwp-agents' ),
'add_new' => __( 'Add Agent', 'arwp-agents' ),
'new_item' => __( 'New Agent', 'arwp-agents' ),
'edit_item' => __( 'Edit Agent', 'arwp-agents' ),
'update_item' => __( 'Update Agent', 'arwp-agents' ),
'view_item' => __( 'View Agent', 'arwp-agents' ),
'view_items' => __( 'View Agents', 'arwp-agents' ),
'search_items' => __( 'Search Agent', 'arwp-agents' ),
'not_found' => __( 'Not found', 'arwp-agents' ),
'not_found_in_trash' => __( 'Not found in Trash', 'arwp-agents' ),
'featured_image' => __( 'Chat Avatar', 'arwp-agents' ),
'set_featured_image' => __( 'Set chat avatar', 'arwp-agents' ),
'remove_featured_image' => __( 'Remove chat avatar', 'arwp-agents' ),
'use_featured_image' => __( 'Use as chat avatar', 'arwp-agents' ),
'insert_into_item' => __( 'Insert into agent', 'arwp-agents' ),
'uploaded_to_this_item' => __( 'Uploaded to this agent', 'arwp-agents' ),
'items_list' => __( 'Agents list', 'arwp-agents' ),
'items_list_navigation' => __( 'Agents list navigation', 'arwp-agents' ),
'filter_items_list' => __( 'Filter agents list', 'arwp-agents' ),
);
$args = array(
'label' => __( 'Agent', 'arwp-agents' ),
'description' => __( 'Chat agents for this site.', 'arwp-agents' ),
'labels' => $labels,
'supports' => array( 'title', 'thumbnail' ),
'hierarchical' => false,
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 26,
'menu_icon' => 'dashicons-format-chat',
'show_in_admin_bar' => true,
'show_in_nav_menus' => false,
'can_export' => true,
'has_archive' => false,
'exclude_from_search' => true,
'publicly_queryable' => false,
'capability_type' => 'post',
'map_meta_cap' => true,
'capabilities' => arwp_agent_capabilities(),
'show_in_rest' => false,
);
register_post_type( 'arwp_agent', $args );
}
/**
* Add Meta Box for arwp_agent custom post type.
*/
public function add_agent_meta_boxes() {
global $wp_meta_boxes;
// Change default 'Publish' metabox title to 'Publish Agent'.
if ( isset( $wp_meta_boxes['arwp_agent']['side']['core']['submitdiv'] ) ) {
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Core has no filter for this metabox title.
$wp_meta_boxes['arwp_agent']['side']['core']['submitdiv']['title'] = __( 'Publish Agent', 'arwp-agents' );
}
add_meta_box(
'arwp_agent_core_settings',
__( 'Agent & Model', 'arwp-agents' ),
array( $this, 'render_agent_core_settings_meta_box' ),
'arwp_agent',
'normal',
'high'
);
add_meta_box(
'arwp_agent_ui_settings',
__( 'Chat Appearance', 'arwp-agents' ),
array( $this, 'render_agent_ui_settings_meta_box' ),
'arwp_agent',
'normal',
'high'
);
add_meta_box(
'arwp_agent_message_checks',
__( 'Message checks', 'arwp-agents' ),
array( $this, 'render_agent_checks_meta_box' ),
'arwp_agent',
'normal',
'default'
);
add_meta_box(
'arwp_agent_shortcode',
__( 'Add to a page', 'arwp-agents' ),
array( $this, 'render_agent_shortcode_meta_box' ),
'arwp_agent',
'side',
'high'
);
}
/**
* Render Agent Shortcode Meta Box.
*
* @param WP_Post $post Current post.
*/
public function render_agent_shortcode_meta_box( $post ) {
if ( 'auto-draft' === $post->post_status || empty( $post->ID ) ) {
echo '' . esc_html__( 'Please save or publish the agent first to generate a shortcode.', 'arwp-agents' ) . '
';
return;
}
$shortcode = '[arwp id="' . intval( $post->ID ) . '"]';
?>
ID, '_arwp_ui_theme', true );
$ui_title = get_post_meta( $post->ID, '_arwp_ui_title', true );
$ui_greeting = get_post_meta( $post->ID, '_arwp_ui_greeting', true );
$ui_placeholder = get_post_meta( $post->ID, '_arwp_ui_placeholder', true );
$primary_color = get_post_meta( $post->ID, '_arwp_ui_primary_color', true );
if ( empty( $ui_theme ) ) {
$ui_theme = 'dark';
}
if ( empty( $ui_title ) ) {
$ui_title = 'AI Assistant';
}
if ( empty( $ui_greeting ) ) {
$ui_greeting = 'Hello! How can I help you today?';
}
if ( empty( $ui_placeholder ) ) {
$ui_placeholder = 'Type your message here...';
}
if ( empty( $primary_color ) ) {
$primary_color = '#3b82f6';
}
?>
ID, '_arwp_blocked_phrases', true );
$patterns = get_post_meta( $post->ID, '_arwp_blocked_patterns', true );
if ( ! is_array( $phrases ) ) {
$phrases = array();
}
if ( ! is_array( $patterns ) ) {
$patterns = array();
}
$phrases = array_values( array_filter( $phrases, 'is_string' ) );
$patterns = array_values( array_filter( $patterns, 'is_string' ) );
?>
ID, '_arwp_system_prompt', true );
$model = get_post_meta( $post->ID, '_arwp_model', true );
$cooldown = get_post_meta( $post->ID, '_arwp_cooldown', true );
$rate_ip = get_post_meta( $post->ID, '_arwp_rate_limit_per_ip', true );
$rate_agent = get_post_meta( $post->ID, '_arwp_rate_limit_per_agent', true );
$history_limit = get_post_meta( $post->ID, '_arwp_history_context_limit', true );
$temperature = get_post_meta( $post->ID, '_arwp_temperature', true );
$max_tokens = get_post_meta( $post->ID, '_arwp_max_tokens', true );
if ( empty( $system_prompt ) ) {
$system_prompt = 'You are a helpful and professional AI assistant.';
}
if ( ! $model ) {
$model = 'default';
}
if ( '' === $cooldown ) {
$cooldown = arwp_default_cooldown();
}
if ( '' === $rate_ip ) {
$rate_ip = arwp_default_rate_limit_per_ip();
}
if ( '' === $rate_agent ) {
$rate_agent = arwp_default_rate_limit_per_agent();
}
if ( '' === $temperature ) {
$temperature = 0.7;
}
if ( '' === $max_tokens ) {
$max_tokens = 1000;
}
$fetched_models = class_exists( 'ARWP_Settings' ) ? ARWP_Settings::get_models() : array();
$models = array(
'default' => __( 'Use Global Default Model', 'arwp-agents' ),
) + $fetched_models;
?>
editable_roles() );
$access_roles = array();
$posted_roles = isset( $_POST['arwp_access_roles'] ) ? map_deep( wp_unslash( $_POST['arwp_access_roles'] ), 'sanitize_text_field' ) : array();
if ( is_array( $posted_roles ) ) {
foreach ( $posted_roles as $role ) {
$role = sanitize_key( $role );
if ( in_array( $role, $valid_roles, true ) && ! in_array( $role, $access_roles, true ) ) {
$access_roles[] = $role;
}
}
}
update_post_meta( $post_id, '_arwp_access_roles', $access_roles );
$rate_user = isset( $_POST['arwp_rate_limit_per_user'] ) ? intval( $_POST['arwp_rate_limit_per_user'] ) : 0;
update_post_meta( $post_id, '_arwp_rate_limit_per_user', min( 10000, max( 0, $rate_user ) ) );
$role_limits = array();
$posted_limits = isset( $_POST['arwp_role_limits'] ) ? map_deep( wp_unslash( $_POST['arwp_role_limits'] ), 'sanitize_text_field' ) : array();
if ( is_array( $posted_limits ) ) {
foreach ( $posted_limits as $role => $limit ) {
$role = sanitize_key( $role );
if ( ! in_array( $role, $valid_roles, true ) ) {
continue;
}
$role_limits[ $role ] = min( 100000, max( 0, intval( $limit ) ) );
}
}
update_post_meta( $post_id, '_arwp_rate_limit_per_role', $role_limits );
if ( isset( $_POST['arwp_agent_ui_meta_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['arwp_agent_ui_meta_nonce'] ) ), 'arwp_save_agent_ui_meta_action' ) ) {
if ( isset( $_POST['arwp_ui_theme'] ) ) {
$theme = sanitize_text_field( wp_unslash( $_POST['arwp_ui_theme'] ) );
if ( ! in_array( $theme, array( 'light', 'dark' ), true ) ) {
$theme = 'dark';
}
update_post_meta( $post_id, '_arwp_ui_theme', $theme );
}
if ( isset( $_POST['arwp_ui_title'] ) ) {
update_post_meta( $post_id, '_arwp_ui_title', sanitize_text_field( wp_unslash( $_POST['arwp_ui_title'] ) ) );
}
if ( isset( $_POST['arwp_ui_greeting'] ) ) {
update_post_meta( $post_id, '_arwp_ui_greeting', sanitize_textarea_field( wp_unslash( $_POST['arwp_ui_greeting'] ) ) );
}
if ( isset( $_POST['arwp_ui_placeholder'] ) ) {
update_post_meta( $post_id, '_arwp_ui_placeholder', sanitize_text_field( wp_unslash( $_POST['arwp_ui_placeholder'] ) ) );
}
if ( isset( $_POST['arwp_ui_primary_color'] ) ) {
$color = sanitize_hex_color( wp_unslash( $_POST['arwp_ui_primary_color'] ) );
if ( $color ) {
update_post_meta( $post_id, '_arwp_ui_primary_color', $color );
}
}
if ( isset( $_POST['arwp_ui_description'] ) ) {
$description = sanitize_text_field( wp_unslash( $_POST['arwp_ui_description'] ) );
update_post_meta( $post_id, '_arwp_ui_description', wp_slash( $this->cut_chars( $description, 180 ) ) );
}
$logo_id = isset( $_POST['arwp_ui_logo_id'] ) ? absint( $_POST['arwp_ui_logo_id'] ) : 0;
if ( $logo_id && ! wp_attachment_is_image( $logo_id ) ) {
$logo_id = 0;
}
update_post_meta( $post_id, '_arwp_ui_logo_id', $logo_id );
$suggestions = isset( $_POST['arwp_ui_suggestions'] ) ? sanitize_textarea_field( wp_unslash( $_POST['arwp_ui_suggestions'] ) ) : '';
update_post_meta( $post_id, '_arwp_ui_suggestions', wp_slash( $this->lines_from_textarea( $suggestions, 6, 120 ) ) );
if ( isset( $_POST['arwp_ui_disclaimer'] ) ) {
update_post_meta( $post_id, '_arwp_ui_disclaimer', wp_slash( sanitize_textarea_field( wp_unslash( $_POST['arwp_ui_disclaimer'] ) ) ) );
}
}
if ( isset( $_POST['arwp_agent_checks_meta_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['arwp_agent_checks_meta_nonce'] ) ), 'arwp_save_agent_checks_meta_action' ) ) {
$phrase_raw = isset( $_POST['arwp_blocked_phrases'] ) ? sanitize_textarea_field( wp_unslash( $_POST['arwp_blocked_phrases'] ) ) : '';
update_post_meta( $post_id, '_arwp_blocked_phrases', wp_slash( $this->lines_from_textarea( $phrase_raw, 20, 80 ) ) );
$pattern_raw = isset( $_POST['arwp_blocked_patterns'] ) ? sanitize_textarea_field( wp_unslash( $_POST['arwp_blocked_patterns'] ) ) : '';
$patterns = array();
$dropped = false;
foreach ( $this->textarea_lines( $pattern_raw ) as $line ) {
$line = sanitize_text_field( $line );
if ( '' === $line ) {
continue;
}
if ( ! arwp_pattern_is_safe( $line ) ) {
$dropped = true;
continue;
}
if ( ! in_array( $line, $patterns, true ) ) {
$patterns[] = $line;
}
if ( count( $patterns ) >= 10 ) {
break;
}
}
update_post_meta( $post_id, '_arwp_blocked_patterns', wp_slash( $patterns ) );
if ( $dropped ) {
set_transient(
'arwp_admin_notice_' . get_current_user_id(),
__( 'Some patterns were not saved because they are not valid.', 'arwp-agents' ),
60
);
}
}
}
/**
* Roles the current user is allowed to assign.
*
* @return array
*/
private function editable_roles() {
if ( ! function_exists( 'get_editable_roles' ) ) {
require_once ABSPATH . 'wp-admin/includes/user.php';
}
$roles = get_editable_roles();
return is_array( $roles ) ? $roles : array();
}
/**
* Cut a string to a character limit.
*
* @param string $text Text.
* @param int $limit Maximum characters.
* @return string
*/
private function cut_chars( $text, $limit ) {
$text = (string) $text;
$limit = (int) $limit;
if ( function_exists( 'mb_substr' ) ) {
return mb_substr( $text, 0, $limit );
}
return substr( $text, 0, $limit );
}
/**
* Split a textarea into trimmed lines.
*
* @param mixed $raw Submitted text.
* @return array
*/
private function textarea_lines( $raw ) {
$raw = str_replace( array( "\r\n", "\r" ), "\n", (string) $raw );
return explode( "\n", $raw );
}
/**
* Keep the first non-empty lines, trimmed and cut to length.
*
* @param mixed $raw Submitted text.
* @param int $max Maximum number of lines.
* @param int $chars Maximum characters per line.
* @return array
*/
private function lines_from_textarea( $raw, $max, $chars ) {
$lines = array();
foreach ( $this->textarea_lines( $raw ) as $line ) {
$line = sanitize_text_field( trim( $line ) );
if ( '' === $line ) {
continue;
}
$line = $this->cut_chars( $line, $chars );
if ( '' === $line || in_array( $line, $lines, true ) ) {
continue;
}
$lines[] = $line;
if ( count( $lines ) >= (int) $max ) {
break;
}
}
return $lines;
}
}