array(
'type' => array(),
'name' => array(),
'id' => array(),
'class' => array(),
'value' => array(),
'placeholder' => array(),
'min' => array(),
'max' => array(),
'step' => array(),
'checked' => array(),
'required' => array(),
'data-*' => true,
),
'select' => array(
'name' => array(),
'id' => array(),
'class' => array(),
'multiple' => array(),
'data-*' => true,
),
'option' => array(
'value' => array(),
'selected' => array(),
),
'optiongroup' => array(
'label' => array(),
),
'span' => array(
'class' => array(),
'id' => array(),
'data-*' => true,
),
'div' => array(
'class' => array(),
'id' => array(),
'data-*' => true,
),
'label' => array(
'for' => array(),
'class' => array(),
),
'p' => array(
'class' => array(),
),
);
// Register custom post type
add_action('init', array($this, 'register_post_type'));
// Add the post type under WooCommerce menu
add_action('admin_menu', array($this, 'add_to_woocommerce_menu'));
// Remove original menu item
add_action('admin_menu', array($this, 'remove_default_menu_item'), 999);
// Add meta box for conditions
add_action('add_meta_boxes', array($this, 'add_conditions_meta_box'));
// Save conditions when post is saved
add_action('save_post_pisol_anfw_notice', array($this, 'save_conditions'), 10, 3);
// Enqueue scripts and styles
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
// Render trigger dropdown
add_action('edit_form_after_title', array($this, 'render_trigger_dropdown'));
}
/**
* Get allowed HTML for condition fields
*
* @return array Allowed HTML tags and attributes
*/
public static function get_allowed_html() {
return self::$allowed_html;
}
/**
* Register the custom post type
*/
public function register_post_type() {
$labels = array(
'name' => _x('Notifications', 'post type general name', 'advance-notification-for-woocommerce'),
'singular_name' => _x('Notification', 'post type singular name', 'advance-notification-for-woocommerce'),
'menu_name' => _x('Notifications', 'admin menu', 'advance-notification-for-woocommerce'),
'name_admin_bar' => _x('Notification', 'add new on admin bar', 'advance-notification-for-woocommerce'),
'add_new' => _x('Add New', 'notification', 'advance-notification-for-woocommerce'),
'add_new_item' => __('Add New Notification', 'advance-notification-for-woocommerce'),
'new_item' => __('New Notification', 'advance-notification-for-woocommerce'),
'edit_item' => __('Edit Notification', 'advance-notification-for-woocommerce'),
'view_item' => __('View Notification', 'advance-notification-for-woocommerce'),
'all_items' => __('All Notifications', 'advance-notification-for-woocommerce'),
'search_items' => __('Search Notifications', 'advance-notification-for-woocommerce'),
'parent_item_colon' => __('Parent Notifications:', 'advance-notification-for-woocommerce'),
'not_found' => __('No notifications found.', 'advance-notification-for-woocommerce'),
'not_found_in_trash' => __('No notifications found in Trash.', 'advance-notification-for-woocommerce'),
);
$args = array(
'labels' => $labels,
'description' => __('Conditional notifications for WooCommerce orders', 'advance-notification-for-woocommerce'),
'public' => false,
'publicly_queryable' => false,
'show_ui' => true,
'show_in_menu' => true, // We'll move this under WooCommerce menu later
'query_var' => false,
'rewrite' => false,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'menu_position' => null,
'supports' => array('title', 'excerpt'), // Added 'excerpt' for short description
'menu_icon' => 'dashicons-tag',
);
register_post_type('pisol_anfw_notice', $args);
}
/**
* Add the post type as a submenu under WooCommerce
*/
public function add_to_woocommerce_menu() {
add_submenu_page(
'advance-notification-for-woocommerce',
__('Create Notifications', 'advance-notification-for-woocommerce'),
__('Create Notifications', 'advance-notification-for-woocommerce'),
'manage_woocommerce',
'edit.php?post_type=pisol_anfw_notice'
);
}
/**
* Remove the default menu item since we're adding it under WooCommerce
*/
public function remove_default_menu_item() {
remove_menu_page('edit.php?post_type=pisol_anfw_notice');
}
/**
* Add meta box for notification conditions
*/
public function add_conditions_meta_box() {
add_meta_box(
'pisol_anfw_notification_conditions',
__('Notification Conditions', 'advance-notification-for-woocommerce'),
array($this, 'render_conditions_meta_box'),
'pisol_anfw_notice',
'normal',
'high'
);
}
/**
* Render the conditions meta box content
*
* @param \WP_Post $post The post object.
*/
public function render_conditions_meta_box($post) {
// Add nonce for security
wp_nonce_field('pisol_anfw_save_conditions', 'pisol_anfw_conditions_nonce');
$selected_trigger = get_post_meta($post->ID, '_pisol_anfw_trigger', true);
if (empty($selected_trigger)) {
echo '
' . esc_html__('Please select and save a Notification Trigger before adding conditions. The conditions metabox will be available after you save.', 'advance-notification-for-woocommerce') . '
';
return;
}
$conditions = get_post_meta($post->ID, '_pisol_anfw_conditions', true);
$conditions = $conditions ? $conditions : array();
$condition_types = $this->get_condition_types($selected_trigger);
$logic_operators = array('AND', 'OR');
// Include the template for conditions form
include plugin_dir_path(__FILE__) . 'templates/conditions-form.php';
}
/**
* Get all available condition types from condition classes
*
* @return array Array of condition types
*/
private function get_condition_types($selected_trigger = '') {
$condition_types = array();
// Apply filter to allow condition classes to register themselves
$flat_condition_types = apply_filters('pisol_anfw_condition_types', array(), $selected_trigger);
// Get the condition objects to organize by group
$condition_objects = apply_filters('pisol_anfw_condition_objects', array(), $selected_trigger);
// Create grouped structure if we have condition objects
if (!empty($condition_objects)) {
$grouped_conditions = array();
// Group conditions by their group property
foreach ($condition_objects as $condition) {
if (method_exists($condition, 'get_id') && method_exists($condition, 'get_name') && method_exists($condition, 'get_group')) {
$group = $condition->get_group();
$id = $condition->get_id();
$name = $condition->get_name();
if (!isset($grouped_conditions[$group])) {
$grouped_conditions[$group] = array();
}
$grouped_conditions[$group][$id] = $name;
}
}
// Sort groups alphabetically
ksort($grouped_conditions);
// Within each group, sort conditions alphabetically
foreach ($grouped_conditions as $group => $conditions) {
asort($conditions);
$grouped_conditions[$group] = $conditions;
}
return $grouped_conditions;
}
// Fallback to flat structure if no condition objects are available
return $flat_condition_types;
}
/**
* Save conditions when the post is saved
*
* @param int $post_id The post ID.
* @param \WP_Post $post The post object.
* @param bool $update Whether this is an existing post being updated.
*/
public function save_conditions($post_id, $post, $update) {
// Check if nonce is valid
if (!isset($_POST['pisol_anfw_conditions_nonce']) || !wp_verify_nonce( sanitize_text_field(wp_unslash($_POST['pisol_anfw_conditions_nonce'])), 'pisol_anfw_save_conditions')) {
return;
}
// Check if user has permission
if (!current_user_can('edit_post', $post_id)) {
return;
}
// Don't save during autosave
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Check if correct post type
if ('pisol_anfw_notice' !== $post->post_type) {
return;
}
// Sanitize and save conditions
if (isset($_POST['pisol_anfw_conditions']) && is_array($_POST['pisol_anfw_conditions'])) {
$raw_conditions = map_deep(wp_unslash($_POST['pisol_anfw_conditions']), 'sanitize_text_field');
$conditions = $this->sanitize_conditions($raw_conditions);
update_post_meta($post_id, '_pisol_anfw_conditions', $conditions);
} else {
delete_post_meta($post_id, '_pisol_anfw_conditions');
}
// Sanitize and save trigger
if (isset($_POST['pisol_anfw_trigger']) && !empty($_POST['pisol_anfw_trigger'])) {
update_post_meta($post_id, '_pisol_anfw_trigger', sanitize_text_field(wp_unslash($_POST['pisol_anfw_trigger'])));
}
}
/**
* Sanitize the conditions array
*
* @param array $conditions The raw conditions array.
* @return array Sanitized conditions.
*/
private function sanitize_conditions($conditions) {
$sanitized = array();
if (!is_array($conditions)) {
return $sanitized;
}
foreach ($conditions as $group_id => $group) {
// Skip empty groups (no conditions or invalid data)
if (empty($group['conditions']) || !is_array($group['conditions'])) {
continue;
}
// Initialize this group in the sanitized array
$sanitized[$group_id] = array(
'match_type' => isset($group['match_type']) && in_array($group['match_type'], array('all', 'any'))
? sanitize_text_field($group['match_type'])
: 'all',
'conditions' => array(),
);
// Add logic operator for group (except first group)
if (isset($group['logic'])) {
$sanitized[$group_id]['logic'] = in_array($group['logic'], array('AND', 'OR'))
? $group['logic']
: 'AND';
}
// Process each condition in this group
foreach ($group['conditions'] as $condition_id => $condition) {
// Skip empty conditions
if (empty($condition['type'])) {
continue;
}
$sanitized[$group_id]['conditions'][$condition_id] = array(
'type' => sanitize_text_field($condition['type']),
'operator' => isset($condition['operator']) ? sanitize_text_field($condition['operator']) : '',
);
// We no longer need logic for individual conditions
// The group's match_type handles this logic now
// Handle different value types (array for multi-select or string for regular fields)
if (isset($condition['value'])) {
if (is_array($condition['value'])) {
// For multi-select fields, sanitize each value and join with comma
$sanitized_values = array_map('sanitize_text_field', $condition['value']);
$sanitized[$group_id]['conditions'][$condition_id]['value'] = $sanitized_values;
} else {
$sanitized[$group_id]['conditions'][$condition_id]['value'] = sanitize_text_field($condition['value']);
}
} else {
$sanitized[$group_id]['conditions'][$condition_id]['value'] = '';
}
}
// If no valid conditions in this group after sanitizing, remove the group
if (empty($sanitized[$group_id]['conditions'])) {
unset($sanitized[$group_id]);
}
}
return $sanitized;
}
/**
* Enqueue scripts and styles for the admin
*
* @param string $hook Current admin page.
*/
public function enqueue_scripts($hook) {
global $post_type;
global $post;
// Only enqueue on our post type edit screens
if ('pisol_anfw_notice' !== $post_type) {
return;
}
$selected_trigger = get_post_meta($post->ID, '_pisol_anfw_trigger', true);
// First check if WooCommerce is active before using WC()
if (!function_exists('WC')) {
return;
}
// Enqueue WooCommerce admin styles with correct handle
wp_enqueue_style('woocommerce_admin_styles');
// Enqueue SelectWoo script without making it a dependency
wp_enqueue_script('selectWoo');
// Enqueue our admin styles with no Select2 dependency
wp_enqueue_style(
'pisol-anfw-admin',
plugin_dir_url(__FILE__) . 'assets/css/admin.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'pisol-anfw-conditions',
plugin_dir_url(__FILE__) . 'assets/js/conditions.js',
array('jquery', 'jquery-ui-sortable', 'wp-util', 'selectWoo'),
'1.0.0',
true
);
// Localize script with data
wp_localize_script(
'pisol-anfw-conditions',
'pisol_anfw_conditions',
array(
'nonce' => wp_create_nonce('pisol_anfw_ajax_nonce'),
'condition_types' => $this->get_condition_types($selected_trigger),
'trigger_type'=> $selected_trigger ,
'texts' => array(
'add_group' => __('Add Condition Group', 'advance-notification-for-woocommerce'),
'add_rule' => __('Add Condition', 'advance-notification-for-woocommerce'),
'remove' => __('Remove', 'advance-notification-for-woocommerce'),
'choose_condition' => __('Select Condition', 'advance-notification-for-woocommerce'),
'confirm_remove_group' => __('Are you sure you want to remove this condition group?', 'advance-notification-for-woocommerce'),
)
)
);
}
/**
* Render the trigger dropdown below the post title
*
* @param WP_Post $post
*/
public function render_trigger_dropdown($post) {
if ($post->post_type !== 'pisol_anfw_notice') {
return;
}
$triggers = apply_filters('pisol_anfw_triggers', array());
$selected_trigger = get_post_meta($post->ID, '_pisol_anfw_trigger', true);
$is_saved = !empty($post->ID) && $selected_trigger;
echo '