.aieo-pro-badge){position:relative}'
. $gallery . '>.aieo-pro-badge{position:absolute;z-index:2;inset-block-start:' . $inset . ';inset-inline-start:' . $inset . '}'
. $gallery . '>.aieo-pro-badge:has(~.aieo-new-badge--single),' . $gallery . '>.aieo-new-badge--single~.aieo-pro-badge{inset-block-start:calc(' . $inset . ' + 2.2rem)}';
}
public function add_badge_layout_body_class($classes) {
$position = AIEO_DMM_Settings::get('archive_badge_position', 'image_top_left');
if ($position === 'below_favourite') {
$classes[] = 'aieo-badges-below-fav';
}
return $classes;
}
const HAS_PRICING_BULK_THRESHOLD = 12;
private $has_pricing_index = null;
private $has_pricing_cache_probed = false;
private $has_pricing_probe = array();
private function pricing_engine() {
if (!class_exists('AIEO_DMM_Pricing_Engine')
|| !is_callable(array('AIEO_DMM_Pricing_Engine', 'instance'))) {
return null;
}
$engine = AIEO_DMM_Pricing_Engine::instance();
return is_object($engine) ? $engine : null;
}
private $role_prices_memo = array();
private function role_pricing_table() {
global $wpdb;
return $wpdb->prefix . 'aieo_dmm_role_prices';
}
public static function has_role_pricing($product_id) {
static $shared = null;
if ($shared === null) { $shared = new self(); }
return $shared->product_has_role_pricing($product_id);
}
private function product_has_role_pricing($product_id) {
$product_id = (int) $product_id;
if ($product_id <= 0) { return false; }
if (!is_array($this->has_pricing_index) && !$this->has_pricing_cache_probed) {
$this->has_pricing_cache_probed = true;
$cached = wp_cache_get($this->has_pricing_cache_key(), 'aieo_dmm');
if (is_array($cached)) { $this->has_pricing_index = $cached; }
}
if (is_array($this->has_pricing_index)) {
return isset($this->has_pricing_index[$product_id]);
}
if (isset($this->has_pricing_probe[$product_id])) {
return $this->has_pricing_probe[$product_id];
}
if (count($this->has_pricing_probe) < self::HAS_PRICING_BULK_THRESHOLD) {
global $wpdb;
$table = $this->role_pricing_table();
$exists = (bool) $wpdb->get_var($wpdb->prepare(
"SELECT 1 FROM {$table} WHERE product_id = %d OR parent_id = %d LIMIT 1",
$product_id, $product_id
));
$this->has_pricing_probe[$product_id] = $exists;
return $exists;
}
$index = $this->get_has_pricing_index();
return isset($index[$product_id]);
}
private function has_pricing_cache_key() {
return 'role_pricing_index_' . self::get_role_prices_version();
}
private function get_has_pricing_index() {
if (is_array($this->has_pricing_index)) {
return $this->has_pricing_index;
}
$key = $this->has_pricing_cache_key();
$cached = wp_cache_get($key, 'aieo_dmm');
if (is_array($cached)) {
$this->has_pricing_index = $cached;
return $this->has_pricing_index;
}
global $wpdb;
$table = $this->role_pricing_table();
$rows = $wpdb->get_results("SELECT product_id, parent_id FROM {$table}", ARRAY_A);
if ('' !== $wpdb->last_error) {
return array();
}
$index = array();
foreach ((array) $rows as $row) {
$index[(int) $row['product_id']] = true;
$parent = (int) $row['parent_id'];
if ($parent > 0) {
$index[$parent] = true;
}
}
$this->has_pricing_index = $index;
wp_cache_set($key, $index, 'aieo_dmm', HOUR_IN_SECONDS);
return $index;
}
private function get_role_prices_for_product($product_id) {
$product_id = (int) $product_id;
if ($product_id <= 0) { return array(); }
if (isset($this->role_prices_memo[$product_id])) {
return $this->role_prices_memo[$product_id];
}
$engine = $this->pricing_engine();
if ($engine && is_callable(array($engine, 'get_cached_role_prices'))) {
$shared = $engine->get_cached_role_prices($product_id);
if (is_array($shared)) {
$this->role_prices_memo[$product_id] = $shared;
return $shared;
}
}
global $wpdb;
$table = $this->role_pricing_table();
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE product_id = %d", $product_id), ARRAY_A);
$out = array();
if (is_array($row)) {
foreach ($row as $col => $val) {
if (strpos($col, 'role_') !== 0) { continue; }
if ($col === 'role_prices') { continue; }
if ($val === null || $val === '') { continue; }
$price = (float) $val;
if ($price > 0) {
$out[substr($col, 5)] = $price;
}
}
}
$this->role_prices_memo[$product_id] = $out;
return $out;
}
private function product_has_real_public_sale($product) {
if (!$product || !is_object($product) || !method_exists($product, 'get_id')) {
return false;
}
$check = static function ($pid) {
$raw_sale = get_post_meta((int) $pid, '_sale_price', true);
if ($raw_sale === '' || $raw_sale === null) { return false; }
$sale = (float) $raw_sale;
if ($sale <= 0) { return false; }
$raw_reg = get_post_meta((int) $pid, '_regular_price', true);
if ($raw_reg === '' || $raw_reg === null) { return false; }
return $sale < (float) $raw_reg;
};
if (method_exists($product, 'is_type') && $product->is_type('variable')) {
foreach ((array) $product->get_children() as $vid) {
if ($check($vid)) { return true; }
}
return $check($product->get_id());
}
return $check($product->get_id());
}
private function current_user_is_pro() {
if (!is_user_logged_in()) { return false; }
if (class_exists('AIEO_DMM_Pricing_Engine') && AIEO_DMM_Pricing_Engine::role_pricing_suppressed()) { return false; }
$user = wp_get_current_user();
if (!$user || empty($user->roles)) { return false; }
$enabled = (array) AIEO_DMM_Settings::get('enabled_roles', array());
if (empty($enabled)) { return false; }
foreach ((array) $user->roles as $r) {
if (in_array($r, $enabled, true)) { return true; }
}
return false;
}
private function best_real_public_sale($product) {
if (!$product || !is_object($product)) { return null; }
$best = null;
$consider = static function ($pid) use (&$best) {
$raw_sale = get_post_meta((int) $pid, '_sale_price', true);
if ($raw_sale === '' || $raw_sale === null) { return; }
$sale = (float) $raw_sale;
if ($sale <= 0) { return; }
$raw_reg = get_post_meta((int) $pid, '_regular_price', true);
if ($raw_reg === '' || $raw_reg === null) { return; }
if ($sale >= (float) $raw_reg) { return; }
if ($best === null || $sale < $best) { $best = $sale; }
};
if (method_exists($product, 'is_type') && $product->is_type('variable')) {
foreach ((array) $product->get_children() as $vid) {
$consider($vid);
}
$consider($product->get_id());
} else {
$consider($product->get_id());
}
return $best;
}
private function best_role_price_for_current_user($product) {
if (!$product || !is_object($product)) { return null; }
if (!$this->current_user_is_pro()) { return null; }
$user = wp_get_current_user();
$user_roles = $user && !empty($user->roles) ? (array) $user->roles : array();
if (empty($user_roles)) { return null; }
$best = null;
$self = $this;
$consider = static function ($pid) use (&$best, $self, $user_roles) {
$prices = $self->get_role_prices_for_product_public((int) $pid);
if (empty($prices)) { return; }
foreach ($user_roles as $role) {
$sanitized = strtolower(preg_replace('/[^a-z0-9_]/i', '_', (string) $role));
if (isset($prices[$sanitized]) && (float) $prices[$sanitized] > 0) {
$p = (float) $prices[$sanitized];
if ($best === null || $p < $best) { $best = $p; }
}
if (isset($prices[$role]) && (float) $prices[$role] > 0) {
$p = (float) $prices[$role];
if ($best === null || $p < $best) { $best = $p; }
}
}
};
if (method_exists($product, 'is_type') && $product->is_type('variable')) {
$children = (array) $product->get_children();
$engine = $this->pricing_engine();
if ($engine && is_callable(array($engine, 'warm_role_price_family'))) {
$engine->warm_role_price_family($product->get_id(), $children);
}
foreach ($children as $vid) {
$consider($vid);
}
$consider($product->get_id());
} else {
$consider($product->get_id());
}
return $best;
}
public function get_role_prices_for_product_public($product_id) {
return $this->get_role_prices_for_product($product_id);
}
private $in_price_filter = false;
public function filter_role_price($price, $product) {
if ($this->in_price_filter) { return $price; }
if (!$product || !is_object($product) || !is_numeric($price) || (float) $price <= 0) {
return $price;
}
if (!$this->current_user_is_pro()) { return $price; }
$this->in_price_filter = true;
$best = $this->best_role_price_for_current_user($product);
$this->in_price_filter = false;
if ($best === null || $best <= 0) { return $price; }
$public = (float) $price;
return ($best < $public) ? $best : $price;
}
public function modify_variation_hash($hash, $product, $for_display) {
if (!is_user_logged_in()) { return $hash; }
if (class_exists('AIEO_DMM_Pricing_Engine') && AIEO_DMM_Pricing_Engine::role_pricing_suppressed()) { return $hash; }
$u = wp_get_current_user();
$roles = $u && !empty($u->roles) ? (array) $u->roles : array();
if (empty($roles)) { return $hash; }
$enabled = (array) AIEO_DMM_Settings::get('enabled_roles', array());
$pricing_roles = array_values(array_intersect($roles, $enabled));
if (empty($pricing_roles)) { return $hash; }
sort($pricing_roles);
if (!is_array($hash)) { $hash = (array) $hash; }
$hash['aieo_role'] = md5(implode('|', $pricing_roles));
$hash['aieo_rpv'] = self::get_role_prices_version();
return $hash;
}
public static function get_role_prices_version() {
return (string) get_option(self::ROLE_PRICES_VERSION_OPTION, '0');
}
public static function bump_role_prices_version() {
$current = (string) get_option(self::ROLE_PRICES_VERSION_OPTION, '0');
$next = (string) time();
if ($next === $current) {
$next .= '.1';
}
wp_cache_delete('role_pricing_index_' . $current, 'aieo_dmm');
update_option(self::ROLE_PRICES_VERSION_OPTION, $next, true);
return $next;
}
public function apply_cart_role_prices($cart) {
if (!is_a($cart, 'WC_Cart')) { return; }
if (!$this->current_user_is_pro()) { return; }
if (did_action('woocommerce_before_calculate_totals') >= 2 && doing_filter('woocommerce_calculate_totals')) {
return;
}
foreach ($cart->get_cart() as $item) {
if (!isset($item['data']) || !is_object($item['data'])) { continue; }
$product = $item['data'];
$best = $this->best_role_price_for_current_user($product);
if ($best === null || $best <= 0) { continue; }
$current = (float) $product->get_price('edit');
if ($best < $current || $current <= 0) {
$product->set_price($best);
}
}
}
private function should_suppress_sale_badge($product) {
if (!$product || !is_object($product)) { return false; }
$has_role = $this->product_has_role_pricing((int) $product->get_id());
if (!$has_role) { return false; }
$public_sale = $this->best_real_public_sale($product);
if ($public_sale === null) { return true; }
if (!$this->current_user_is_pro()) { return false; }
$role_price = $this->best_role_price_for_current_user($product);
if ($role_price === null) { return false; }
return $role_price < $public_sale;
}
private function should_suppress_pro_badge($product) {
if (!$product || !is_object($product)) { return true; }
if (!$this->product_has_role_pricing((int) $product->get_id())) { return true; }
if (!$this->current_user_is_pro()) { return false; }
$public_sale = $this->best_real_public_sale($product);
if ($public_sale === null) { return false; }
$role_price = $this->best_role_price_for_current_user($product);
if ($role_price === null) { return false; }
return $role_price >= $public_sale;
}
public function show_pro_price_as_sale($price_html, $product) {
if (!is_a($product, 'WC_Product') || !$this->current_user_is_pro()) {
return $price_html;
}
if ($product->is_type('variable') || $product->is_type('grouped')) {
return $price_html;
}
$role_price = $this->best_role_price_for_current_user($product);
if ($role_price === null || (float) $role_price <= 0) {
return $price_html;
}
$public_regular = (float) str_replace(',', '.', (string) get_post_meta((int) $product->get_id(), '_regular_price', true));
if ($public_regular <= 0 || (float) $role_price >= $public_regular) {
return $price_html;
}
if (abs((float) $product->get_price() - (float) $role_price) > 0.0001) {
return $price_html;
}
$reg_display = wc_get_price_to_display($product, array('price' => $public_regular));
$role_display = wc_get_price_to_display($product, array('price' => (float) $role_price));
return wc_format_sale_price($reg_display, $role_display) . $product->get_price_suffix();
}
public function maybe_suppress_sale_flash($html, $post, $product) {
return $this->should_suppress_sale_badge($product) ? '' : $html;
}
public function rewire_login_message_renderer() {
if (!class_exists('OM_Role_Login_Message') || !class_exists('OM_Role_Price_Handler')) {
return;
}
$msg = OM_Role_Login_Message::instance(OM_Role_Price_Handler::instance());
if (!is_object($msg)) {
return;
}
remove_filter('woocommerce_get_price_html', array($msg, 'add_login_message'), 99);
}
public function append_login_message_to_price($price_html, $product) {
if (is_user_logged_in()) {
return $price_html;
}
if (empty(AIEO_DMM_Settings::get('show_login_message', false))) {
return $price_html;
}
if (!$product || !is_a($product, 'WC_Product')) {
return $price_html;
}
if (!$this->product_has_role_pricing((int) $product->get_id())) {
return $price_html;
}
if (!empty($GLOBALS['aieo_satc_suppress_login_message'])) {
return $price_html;
}
$is_single = (function_exists('is_product') && is_product());
if ($is_single && self::login_in_cluster()) {
return $price_html;
}
$key = $is_single ? 'login_message_text' : 'login_message_archive';
$raw = (string) AIEO_DMM_Settings::get($key, '');
if ($raw === '') {
return $price_html;
}
$login_url = function_exists('wc_get_page_permalink') ? (string) wc_get_page_permalink('myaccount') : wp_login_url();
$raw = str_replace('{login_url}', esc_url($login_url), $raw);
$custom_class = (string) AIEO_DMM_Settings::get('login_message_css_class', '');
$classes = 'om-role-login-message ' . ($is_single ? 'om-login-message-single' : 'om-login-message-archive');
if ($custom_class !== '') {
$classes .= ' ' . $custom_class;
}
return $price_html . sprintf(
'%s',
esc_attr($classes),
$raw
);
}
public function print_login_message_css() {
if (is_user_logged_in()) { return; }
if (empty(AIEO_DMM_Settings::get('show_login_message', false))) { return; }
$color = (string) sanitize_hex_color((string) AIEO_DMM_Settings::get('login_message_color', ''));
$ad = (int) AIEO_DMM_Settings::get('login_message_font_archive_desktop', 0);
$am = (int) AIEO_DMM_Settings::get('login_message_font_archive_mobile', 0);
$sd = (int) AIEO_DMM_Settings::get('login_message_font_single_desktop', 0);
$sm = (int) AIEO_DMM_Settings::get('login_message_font_single_mobile', 0);
if ($color === '' && $ad <= 0 && $am <= 0 && $sd <= 0 && $sm <= 0) {
return;
}
$css = '';
if ($color !== '') {
$css .= '.om-role-login-message a{color:' . $color . '}';
}
if ($ad > 0) {
$css .= '.om-login-message-archive,.om-login-message-archive a{font-size:' . $ad . 'px}';
}
if ($sd > 0) {
$css .= '.om-login-message-single,.om-login-message-single a{font-size:' . $sd . 'px}';
}
$mq = '';
if ($am > 0) {
$mq .= '.om-login-message-archive,.om-login-message-archive a{font-size:' . $am . 'px}';
}
if ($sm > 0) {
$mq .= '.om-login-message-single,.om-login-message-single a{font-size:' . $sm . 'px}';
}
if ($mq !== '') {
$css .= '@media(max-width:768px){' . $mq . '}';
}
echo '';
}
public function render_login_modal() {
if (is_user_logged_in()) { return; }
if (empty(AIEO_DMM_Settings::get('show_login_message', false))) { return; }
if (empty(AIEO_DMM_Settings::get('login_message_modal_enabled', false))) { return; }
if (function_exists('is_account_page') && is_account_page()) { return; }
$is_ctx = (function_exists('is_woocommerce') && is_woocommerce())
|| (function_exists('is_shop') && is_shop())
|| is_front_page() || is_home();
if (!$is_ctx) { return; }
$current = esc_url_raw(home_url(wp_unslash($_SERVER['REQUEST_URI'] ?? '/')));
$login_html = '';
if (function_exists('woocommerce_login_form')) {
ob_start();
woocommerce_login_form(array('redirect' => $current));
$login_html = (string) ob_get_clean();
}
$sso_html = class_exists('AIEO_Runtime_SSO_Buttons')
? (string) AIEO_Runtime_SSO_Buttons::render('login', array(
'layout' => 'stacked',
'intro' => __('Or sign in with:', 'ai-eshop-optimizer'),
))
: '';
$rich = (string) AIEO_DMM_Settings::get('login_message_modal_content', '');
if ($login_html === '' && $sso_html === '' && trim($rich) === '') { return; }
?>
20, 'height' => 20, 'class' => 'aieo-rolelogin-modal__close-icon'));
if ('' !== $icon) {
return $icon;
}
}
return class_exists('AIEO_Icons') ? AIEO_Icons::svg('x', '20px') : '';
}
public static function login_in_cluster() {
$o = get_option('aieo_dmm_role_pricing_login_settings', array());
return is_array($o) && !empty($o['enabled']);
}
public function render_cluster_login_message() {
if (!self::login_in_cluster()) { return; }
if (is_user_logged_in()) { return; }
if (empty(AIEO_DMM_Settings::get('show_login_message', false))) { return; }
if (!function_exists('is_product') || !is_product()) { return; }
global $product;
if (!$product || !is_a($product, 'WC_Product')) { $product = wc_get_product(get_the_ID()); }
if (!$product || !is_a($product, 'WC_Product')) { return; }
if (!$this->product_has_role_pricing((int) $product->get_id())) { return; }
$raw = (string) AIEO_DMM_Settings::get('login_message_text', '');
if ($raw === '') { return; }
$login_url = function_exists('wc_get_page_permalink') ? (string) wc_get_page_permalink('myaccount') : wp_login_url();
$raw = str_replace('{login_url}', esc_url($login_url), $raw);
$custom_class = (string) AIEO_DMM_Settings::get('login_message_css_class', '');
$classes = 'om-role-login-message om-login-message-single om-login-message-cluster' . ($custom_class !== '' ? ' ' . $custom_class : '');
echo '' . $raw . '';
}
public function maybe_suppress_om_ppe_sale_label($display, $product, $rule) {
if (!$display) { return $display; }
if ($rule !== 'products_on_sale') { return $display; }
return $this->should_suppress_sale_badge($product) ? false : $display;
}
public function render_archive_pro_badge() {
global $product;
if (!$product || !is_a($product, 'WC_Product')) { return; }
if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::picture_carries($product->get_id())) { return; }
echo $this->archive_pro_badge_html($product);
}
public function archive_pro_badge_html($product) {
if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return ''; }
if (!$product || !is_a($product, 'WC_Product') || $this->should_suppress_pro_badge($product)) { return ''; }
self::pro_badge_style();
return $this->pro_badge_html();
}
private function pro_badge_html() {
$label = apply_filters('aieo_dmm_pro_badge_label', __('PRO', 'ai-eshop-optimizer'));
$tooltip = apply_filters(
'aieo_dmm_pro_badge_tooltip',
__('This product has special pricing for professionals', 'ai-eshop-optimizer')
);
return ''
. ''
. '' . esc_html($label) . ''
. '';
}
public function render_single_pro_badge() {
if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return; }
$html = $this->build_single_pro_badge_html();
if ($html !== '') {
echo $html;
}
}
private function build_single_pro_badge_html() {
global $product;
if (!$product || !is_a($product, 'WC_Product')) { return ''; }
if ($this->should_suppress_pro_badge($product)) { return ''; }
return $this->pro_badge_html();
}
public function inject_pro_badge_into_gallery($block_content, $block) {
if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return $block_content; }
if (!is_singular('product')) { return $block_content; }
$badge = $this->build_single_pro_badge_html();
if ($badge === '') { return $block_content; }
if (preg_match('/^(\s*<[a-zA-Z][^>]*>)(.*)$/s', (string) $block_content, $m)) {
return $m[1] . $badge . $m[2];
}
return $badge . $block_content;
}
public function render_subtab() {
$per_page = 25;
$current_page = isset($_GET['paged']) ? max(1, (int) $_GET['paged']) : 1;
$this->render_template('role-pricing.php', array(
'settings' => AIEO_DMM_Settings::get_all(),
'all_roles' => $this->get_available_roles(),
'total_rows' => $this->count_priced_rows(),
'price_rows' => $this->fetch_priced_rows($per_page, $current_page),
'per_page' => $per_page,
'current_page' => $current_page,
'gf_forms' => $this->gf_available_forms_for_select(),
'gf_active' => class_exists('GFAPI'),
'gf_samples_url' => plugins_url('assets/samples/', AIEO_FILE),
));
}
private function gf_available_forms_for_select() {
if (!class_exists('GFAPI')) { return array(); }
$forms = GFAPI::get_forms(true, false);
$out = array();
if (is_array($forms)) {
foreach ($forms as $f) {
if (isset($f['id'])) {
$out[] = array(
'id' => (int) $f['id'],
'title' => isset($f['title']) ? (string) $f['title'] : ('Form #' . (int) $f['id']),
);
}
}
}
return $out;
}
public function ajax_fetch_rows() {
AIEO_DMM::verify_ajax_request();
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_role_prices';
if (!aieo_dmm_table_exists($table)) {
wp_send_json_success(array('rows' => array(), 'total' => 0, 'filtered' => 0, 'columns' => array()));
}
$enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
$role_cols = array();
foreach ($enabled_roles as $role) {
$role_cols[$role] = aieo_dmm_sanitize_role_column($role);
}
$page = isset($_POST['page']) ? max(1, absint($_POST['page'])) : 1;
$per_page = isset($_POST['per_page']) ? min(200, max(10, absint($_POST['per_page']))) : 25;
$search = isset($_POST['search']) ? trim(sanitize_text_field(wp_unslash($_POST['search']))) : '';
$only_nonempty = !empty($_POST['only_nonempty']);
$sort_col_req = isset($_POST['sort']) ? sanitize_key(wp_unslash($_POST['sort'])) : 'product_id';
$dir_req = isset($_POST['dir']) && sanitize_key(wp_unslash($_POST['dir'])) === 'desc' ? 'DESC' : 'ASC';
$allowed_sort = array('product_id' => 'product_id', 'sku' => 'sku', 'product_type' => 'product_type');
foreach ($role_cols as $role => $col) {
$allowed_sort[$role] = $col;
}
$sort_col = isset($allowed_sort[$sort_col_req]) ? $allowed_sort[$sort_col_req] : 'product_id';
$where = array('1=1');
$params = array();
if ($search !== '') {
$where[] = '(sku LIKE %s OR CAST(product_id AS CHAR) LIKE %s)';
$like = '%' . $wpdb->esc_like($search) . '%';
$params[] = $like;
$params[] = $like;
}
if ($only_nonempty && !empty($role_cols)) {
$non_null = array();
foreach ($role_cols as $col) {
$non_null[] = "(`{$col}` IS NOT NULL AND `{$col}` > 0)";
}
$where[] = '(' . implode(' OR ', $non_null) . ')';
}
$where_sql = 'WHERE ' . implode(' AND ', $where);
$total = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
$filtered_sql = "SELECT COUNT(*) FROM {$table} {$where_sql}";
$filtered = (int) $wpdb->get_var(empty($params) ? $filtered_sql : $wpdb->prepare($filtered_sql, $params));
$offset = ($page - 1) * $per_page;
$data_sql = "SELECT * FROM {$table} {$where_sql} ORDER BY `{$sort_col}` {$dir_req} LIMIT %d OFFSET %d";
$data_params = array_merge($params, array($per_page, $offset));
$rows = $wpdb->get_results($wpdb->prepare($data_sql, $data_params), ARRAY_A);
if (!is_array($rows)) {
$rows = array();
}
$columns = array(
array('key' => 'product_id', 'label' => __('Product ID', 'ai-eshop-optimizer'), 'sortable' => true, 'editable' => false),
array('key' => 'sku', 'label' => __('SKU', 'ai-eshop-optimizer'), 'sortable' => true, 'editable' => false),
array('key' => 'product_type', 'label' => __('Type', 'ai-eshop-optimizer'), 'sortable' => true, 'editable' => false),
);
foreach ($enabled_roles as $role) {
$columns[] = array('key' => $role, 'label' => $role, 'sortable' => true, 'editable' => true);
}
$out = array();
foreach ($rows as $r) {
$item = array(
'product_id' => (int) $r['product_id'],
'sku' => (string) ($r['sku'] ?? ''),
'product_type' => (string) ($r['product_type'] ?? ''),
);
foreach ($role_cols as $role => $col) {
$item[$role] = (isset($r[$col]) && $r[$col] !== null) ? (float) $r[$col] : null;
}
$out[] = $item;
}
wp_send_json_success(array(
'columns' => $columns,
'rows' => $out,
'total' => $total,
'filtered' => $filtered,
'page' => $page,
'per_page' => $per_page,
));
}
public function ajax_save_cell() {
AIEO_DMM::verify_ajax_request();
$product_id = isset($_POST['product_id']) ? absint(wp_unslash($_POST['product_id'])) : 0;
$role = isset($_POST['role']) ? sanitize_key(wp_unslash($_POST['role'])) : '';
$raw_value = isset($_POST['value']) ? trim(sanitize_text_field(wp_unslash($_POST['value']))) : '';
if ($product_id <= 0 || $role === '') {
wp_send_json_error(array('message' => __('Missing product_id or role.', 'ai-eshop-optimizer')));
}
$enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
if (!in_array($role, $enabled_roles, true)) {
wp_send_json_error(array('message' => __('That role is not enabled.', 'ai-eshop-optimizer')));
}
$is_empty = $raw_value === '' || $raw_value === '-';
if (!$is_empty) {
$val = str_replace(',', '.', $raw_value);
if (!is_numeric($val) || (float) $val < 0) {
wp_send_json_error(array('message' => __('Value must be a non-negative number.', 'ai-eshop-optimizer')));
}
$val = (float) $val;
} else {
$val = null;
}
$col = aieo_dmm_sanitize_role_column($role);
global $wpdb;
$tables = array(
$wpdb->prefix . 'aieo_dmm_role_prices',
$wpdb->prefix . 'om_role_based_pricing',
);
foreach ($tables as $table) {
if (!aieo_dmm_table_exists($table)) {
continue;
}
$exists = $wpdb->get_var($wpdb->prepare(
"SELECT 1 FROM {$table} WHERE product_id = %d LIMIT 1",
$product_id
));
if ($exists) {
$wpdb->query($wpdb->prepare(
"UPDATE {$table} SET `{$col}` = " . ($is_empty ? 'NULL' : '%f') . " WHERE product_id = %d",
$is_empty ? array($product_id) : array($val, $product_id)
));
} else {
$product = function_exists('wc_get_product') ? wc_get_product($product_id) : null;
$sku = $product ? (string) $product->get_sku() : '';
$parent_id = $product && $product->is_type('variation') ? (int) $product->get_parent_id() : $product_id;
$type = $product && $product->is_type('variation') ? 'variation' : 'simple';
$insert = array(
'product_id' => $product_id,
'parent_id' => $parent_id,
'product_type' => $type,
'sku' => $sku,
);
$fmt = array('%d', '%d', '%s', '%s');
if (!$is_empty) {
$insert[$col] = $val;
$fmt[] = '%f';
}
$wpdb->insert($table, $insert, $fmt);
}
}
AIEO_DMM_Cache::bump_version('prices');
self::bump_role_prices_version();
wp_send_json_success(array(
'product_id' => $product_id,
'role' => $role,
'value' => $is_empty ? null : $val,
));
}
public function get_available_roles() {
if (!function_exists('get_editable_roles')) {
require_once ABSPATH . 'wp-admin/includes/user.php';
}
$roles = get_editable_roles();
$out = array();
foreach ($roles as $slug => $data) {
if ($slug === 'administrator') {
continue;
}
$out[$slug] = isset($data['name']) ? translate_user_role($data['name']) : $slug;
}
return $out;
}
public function ajax_save_settings() {
AIEO_DMM::verify_ajax_request();
$raw_roles = isset($_POST['enabled_roles']) && is_array($_POST['enabled_roles'])
? array_map('sanitize_key', wp_unslash($_POST['enabled_roles']))
: array();
$roles = array();
foreach ($raw_roles as $r) {
$k = sanitize_key($r);
if ($k !== '' && $k !== 'administrator') {
$roles[] = $k;
}
}
$badge_position = isset($_POST['archive_badge_position']) ? sanitize_key(wp_unslash($_POST['archive_badge_position'])) : 'image_top_left';
if (!in_array($badge_position, array('image_top_left', 'below_favourite'), true)) {
$badge_position = 'image_top_left';
}
AIEO_DMM_Settings::update(array(
'enabled_roles' => $roles,
'show_login_message' => !empty($_POST['show_login_message']),
'login_message_text' => isset($_POST['login_message_text']) ? wp_kses_post(wp_unslash($_POST['login_message_text'])) : '',
'login_message_archive' => isset($_POST['login_message_archive']) ? wp_kses_post(wp_unslash($_POST['login_message_archive'])) : '',
'login_message_css_class'=> isset($_POST['login_message_css_class']) ? sanitize_html_class(wp_unslash($_POST['login_message_css_class']), '') : '',
'login_message_color' => isset($_POST['login_message_color']) ? (string) sanitize_hex_color(wp_unslash($_POST['login_message_color'])) : '',
'login_message_font_archive_desktop' => isset($_POST['login_message_font_archive_desktop']) ? max(0, min(72, (int) $_POST['login_message_font_archive_desktop'])) : 0,
'login_message_font_archive_mobile' => isset($_POST['login_message_font_archive_mobile']) ? max(0, min(72, (int) $_POST['login_message_font_archive_mobile'])) : 0,
'login_message_font_single_desktop' => isset($_POST['login_message_font_single_desktop']) ? max(0, min(72, (int) $_POST['login_message_font_single_desktop'])) : 0,
'login_message_font_single_mobile' => isset($_POST['login_message_font_single_mobile']) ? max(0, min(72, (int) $_POST['login_message_font_single_mobile'])) : 0,
'login_message_modal_enabled' => !empty($_POST['login_message_modal_enabled']),
'login_message_modal_content' => isset($_POST['login_message_modal_content']) ? wp_kses_post(wp_unslash($_POST['login_message_modal_content'])) : '',
'mini_cart_badge_icon' => isset($_POST['mini_cart_badge_icon']) ? wp_kses_post(wp_unslash($_POST['mini_cart_badge_icon'])) : '',
'mini_cart_badge_text' => isset($_POST['mini_cart_badge_text']) ? sanitize_text_field(wp_unslash($_POST['mini_cart_badge_text'])) : '',
'mini_cart_badge_alt' => isset($_POST['mini_cart_badge_alt']) ? wp_kses_post(wp_unslash($_POST['mini_cart_badge_alt'])) : '',
'wpdatatables_table_id' => isset($_POST['wpdatatables_table_id']) ? absint(wp_unslash($_POST['wpdatatables_table_id'])) : 0,
'archive_badge_position' => $badge_position,
));
update_option('aieo_dmm_role_pricing_login_settings', array(
'enabled' => !empty($_POST['login_message_in_cluster']),
), false);
if (function_exists('aieo_dmm_sync_role_columns')) {
aieo_dmm_sync_role_columns($roles);
}
AIEO_DMM_Settings::sync_legacy_role_pricing_option();
AIEO_DMM_Cache::bump_version('prices');
self::bump_role_prices_version();
wp_send_json_success(array(
'message' => __('Settings saved.', 'ai-eshop-optimizer'),
'enabled_roles' => $roles,
));
}
public function ajax_upload_csv() {
AIEO_DMM::verify_ajax_request();
if (empty($_FILES['csv_file']) || !isset($_FILES['csv_file']['tmp_name'])) {
wp_send_json_error(array('message' => __('No file received.', 'ai-eshop-optimizer')));
}
$mode = isset($_POST['mode']) && sanitize_key(wp_unslash($_POST['mode'])) === 'overwrite' ? 'overwrite' : 'merge';
$file = $_FILES['csv_file'];
$tmp = isset($file['tmp_name']) ? $file['tmp_name'] : '';
if (!is_uploaded_file($tmp)) {
wp_send_json_error(array('message' => __('Upload validation failed.', 'ai-eshop-optimizer')));
}
$raw = file_get_contents($tmp);
if (is_string($raw) && substr($raw, 0, 3) === "\xEF\xBB\xBF") {
file_put_contents($tmp, substr($raw, 3));
}
$first_line = '';
if ($peek = fopen($tmp, 'r')) {
$first_line = (string) fgets($peek);
fclose($peek);
}
$delim = ',';
if ($first_line !== '') {
$commas = substr_count($first_line, ',');
$semis = substr_count($first_line, ';');
$tabs = substr_count($first_line, "\t");
if ($semis > $commas && $semis >= $tabs) { $delim = ';'; }
elseif ($tabs > $commas && $tabs > $semis) { $delim = "\t"; }
}
$handle = fopen($tmp, 'r');
if (!$handle) {
wp_send_json_error(array('message' => __('Cannot read uploaded file.', 'ai-eshop-optimizer')));
}
$header = fgetcsv($handle, 0, $delim);
if (!$header || !is_array($header) || count($header) < 2) {
fclose($handle);
wp_send_json_error(array('message' => __('CSV must have at least a sku column and one role column. (Detected separator: ', 'ai-eshop-optimizer') . ($delim === "\t" ? 'TAB' : $delim) . ')'));
}
$header_map = array();
foreach ($header as $idx => $col) {
$clean = strtolower(trim(str_replace("\xEF\xBB\xBF", '', (string) $col)));
$header_map[$clean] = $idx;
}
if (!isset($header_map['sku'])) {
$detected = $delim === "\t" ? 'TAB' : $delim;
$found = implode(', ', array_keys($header_map));
fclose($handle);
wp_send_json_error(array(
'message' => sprintf(
__('CSV must include a sku column. Detected separator: %1$s. Columns found in header: %2$s', 'ai-eshop-optimizer'),
$detected,
$found
),
));
}
$sku_idx = $header_map['sku'];
unset($header_map['sku']);
$enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
$role_map = array();
foreach ($header_map as $name => $idx) {
foreach ($enabled_roles as $role) {
if (strtolower($role) === $name) {
$role_map[$role] = $idx;
break;
}
}
}
if (empty($role_map)) {
fclose($handle);
wp_send_json_error(array('message' => __('No CSV column matches an enabled role. Enable roles first.', 'ai-eshop-optimizer')));
}
$rows = array();
while (($row = fgetcsv($handle, 0, $delim)) !== false) {
if (!is_array($row)) {
continue;
}
$sku = isset($row[$sku_idx]) ? trim((string) $row[$sku_idx]) : '';
if ($sku === '') {
continue;
}
$prices = array();
foreach ($role_map as $role => $idx) {
$v = isset($row[$idx]) ? (string) $row[$idx] : '';
$v = trim(str_replace(',', '.', $v));
if ($v === '' || !is_numeric($v) || (float) $v <= 0) {
continue;
}
$prices[$role] = (float) $v;
}
if (!empty($prices)) {
$rows[] = array('sku' => $sku, 'prices' => $prices);
}
}
fclose($handle);
if (empty($rows)) {
wp_send_json_error(array('message' => __('CSV had no valid rows.', 'ai-eshop-optimizer')));
}
$job_id = wp_generate_password(16, false, false);
$this->create_job($job_id, 'role_pricing_import', count($rows), array(
'mode' => $mode,
'rows' => $rows,
'roles' => array_keys($role_map),
));
wp_send_json_success(array(
'job_id' => $job_id,
'total' => count($rows),
'batch_size' => 100,
));
}
public function ajax_process_batch() {
AIEO_DMM::verify_ajax_request();
$job_id = isset($_POST['job_id']) ? sanitize_text_field(wp_unslash($_POST['job_id'])) : '';
$job = $this->load_job($job_id);
if (!$job || $job['type'] !== 'role_pricing_import') {
wp_send_json_error(array('message' => __('Invalid job.', 'ai-eshop-optimizer')));
}
$params = json_decode($job['params'], true);
if (!is_array($params) || !isset($params['rows']) || !is_array($params['rows'])) {
wp_send_json_error(array('message' => __('Corrupted job state.', 'ai-eshop-optimizer')));
}
$mode = isset($params['mode']) && $params['mode'] === 'overwrite' ? 'overwrite' : 'merge';
$batch_size = 100;
$offset = (int) $job['processed'];
if ($offset === 0 && $mode === 'overwrite') {
global $wpdb;
$t_new = $wpdb->prefix . 'aieo_dmm_role_prices';
$t_old = $wpdb->prefix . 'om_role_based_pricing';
$wpdb->query("TRUNCATE TABLE {$t_new}");
if (aieo_dmm_table_exists($t_old)) {
$wpdb->query("TRUNCATE TABLE {$t_old}");
}
self::bump_role_prices_version();
}
$slice = array_slice($params['rows'], $offset, $batch_size);
$errors = array();
$written = 0;
foreach ($slice as $row) {
$result = $this->write_row($row['sku'], $row['prices']);
if ($result['ok']) {
$written++;
} else {
$errors[] = $result['error'];
}
}
$new_processed = $offset + count($slice);
$complete = $new_processed >= (int) $job['total'];
$this->update_job($job_id, array(
'processed' => $new_processed,
'status' => $complete ? 'complete' : 'running',
));
if ($complete) {
AIEO_DMM_Cache::bump_version('prices');
self::bump_role_prices_version();
}
wp_send_json_success(array(
'processed' => $new_processed,
'total' => (int) $job['total'],
'written' => $written,
'errors' => array_slice($errors, 0, 20),
'complete' => $complete,
'percentage' => (int) $job['total'] > 0 ? (int) floor(($new_processed / (int) $job['total']) * 100) : 100,
));
}
private function write_row($sku, array $prices) {
global $wpdb;
$product_id = function_exists('wc_get_product_id_by_sku') ? (int) wc_get_product_id_by_sku($sku) : 0;
if ($product_id <= 0) {
return array('ok' => false, 'error' => sprintf(
__('SKU %s not found.', 'ai-eshop-optimizer'),
$sku
));
}
$parent_id = $product_id;
$type = 'simple';
$post = get_post($product_id);
if ($post && $post->post_type === 'product_variation') {
$parent_id = (int) $post->post_parent;
$type = 'variation';
}
$row_base = array(
'parent_id' => $parent_id,
'product_type' => $type,
'sku' => (string) $sku,
);
$row_formats = array('%d', '%s', '%s');
foreach ($prices as $role => $price) {
$col = aieo_dmm_sanitize_role_column((string) $role);
$row_base[$col] = (float) $price;
$row_formats[] = '%f';
}
$tables = array(
$wpdb->prefix . 'aieo_dmm_role_prices',
$wpdb->prefix . 'om_role_based_pricing',
);
foreach ($tables as $table) {
if (!aieo_dmm_table_exists($table)) {
continue;
}
$exists = $wpdb->get_var($wpdb->prepare(
"SELECT 1 FROM {$table} WHERE product_id = %d LIMIT 1",
$product_id
));
if ($exists) {
$wpdb->update($table, $row_base, array('product_id' => $product_id), $row_formats, array('%d'));
} else {
$row_with_pk = array('product_id' => $product_id) + $row_base;
$formats_with_pk = array_merge(array('%d'), $row_formats);
$wpdb->insert($table, $row_with_pk, $formats_with_pk);
}
}
return array('ok' => true);
}
public function ajax_export_csv() {
AIEO_DMM::verify_ajax_request();
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_role_prices';
if (!aieo_dmm_table_exists($table)) {
wp_send_json_error(array('message' => __('No role pricing data yet.', 'ai-eshop-optimizer')));
}
$enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
if (empty($enabled_roles)) {
wp_send_json_error(array('message' => __('Enable at least one role first.', 'ai-eshop-optimizer')));
}
$cols = array('sku');
foreach ($enabled_roles as $role) {
$cols[] = $role;
}
$db_cols = array('sku');
foreach ($enabled_roles as $role) {
$db_cols[] = aieo_dmm_sanitize_role_column($role);
}
$select = implode(', ', array_map(function ($c) { return '`' . $c . '`'; }, $db_cols));
$rows = $wpdb->get_results("SELECT {$select} FROM {$table} ORDER BY product_id ASC", ARRAY_N);
$dir = AIEO_DMM::get_upload_path('exports');
$filename = 'role-prices-' . gmdate('Ymd-His') . '.csv';
$path = trailingslashit($dir['path']) . $filename;
$url = trailingslashit($dir['url']) . $filename;
$handle = fopen($path, 'w');
if (!$handle) {
wp_send_json_error(array('message' => __('Cannot write export file.', 'ai-eshop-optimizer')));
}
fputcsv($handle, $cols);
foreach ((array) $rows as $row) {
fputcsv($handle, $row);
}
fclose($handle);
wp_send_json_success(array(
'url' => $url,
'filename' => $filename,
'rows' => is_array($rows) ? count($rows) : 0,
));
}
public function ajax_download_template() {
AIEO_DMM::verify_ajax_request();
$roles = AIEO_DMM_Settings::get('enabled_roles', array());
$cols = array_merge(array('sku'), $roles);
$samples = $this->build_template_sample_rows($roles, 3);
$dir = AIEO_DMM::get_upload_path('exports');
$filename = 'role-prices-template.csv';
$path = trailingslashit($dir['path']) . $filename;
$url = trailingslashit($dir['url']) . $filename;
$handle = fopen($path, 'w');
if (!$handle) {
wp_send_json_error(array('message' => __('Cannot write template file.', 'ai-eshop-optimizer')));
}
AIEO_DMM_Sample::write_csv_bom($handle);
fputcsv($handle, $cols);
foreach ($samples as $row) {
fputcsv($handle, $row);
}
fclose($handle);
wp_send_json_success(array('url' => $url, 'filename' => $filename));
}
private function build_template_sample_rows(array $roles, $count = 3) {
$rows = array();
$product_skus = array();
if (function_exists('wc_get_products')) {
$products = wc_get_products(array(
'status' => 'publish',
'limit' => max(1, (int) $count),
'orderby' => 'date',
'order' => 'DESC',
));
foreach ($products as $p) {
$sku = (string) $p->get_sku();
if ($sku !== '') {
$product_skus[] = $sku;
}
if (count($product_skus) >= (int) $count) {
break;
}
}
}
if (empty($product_skus)) {
$product_skus = array('EXAMPLE-SKU-1', 'EXAMPLE-SKU-2', 'EXAMPLE-SKU-3');
}
$base_prices = array(19.99, 24.50, 9.90);
foreach ($product_skus as $idx => $sku) {
$row = array($sku);
$base = isset($base_prices[$idx]) ? $base_prices[$idx] : 9.99;
foreach ($roles as $n => $role) {
$price = max(0.01, $base - ($n * 1.5));
$row[] = number_format($price, 2, '.', '');
}
$rows[] = $row;
if (count($rows) >= (int) $count) {
break;
}
}
return $rows;
}
private function count_price_rows() {
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_role_prices';
if (!aieo_dmm_table_exists($table)) {
return 0;
}
return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
}
private function priced_rows_where_clause() {
$roles = (array) AIEO_DMM_Settings::get('enabled_roles', array());
if (empty($roles)) { return '1=0'; }
$parts = array();
foreach ($roles as $role) {
$col = aieo_dmm_sanitize_role_column($role);
$parts[] = "(`{$col}` IS NOT NULL AND `{$col}` > 0)";
}
return '(' . implode(' OR ', $parts) . ')';
}
private function count_priced_rows() {
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_role_prices';
if (!aieo_dmm_table_exists($table)) {
return 0;
}
$where = $this->priced_rows_where_clause();
return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$where}");
}
private function fetch_priced_rows($per_page, $page) {
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_role_prices';
if (!aieo_dmm_table_exists($table)) {
return array();
}
$where = $this->priced_rows_where_clause();
$offset = max(0, ((int) $page - 1) * (int) $per_page);
$sql = $wpdb->prepare(
"SELECT * FROM {$table} WHERE {$where} ORDER BY product_id ASC LIMIT %d OFFSET %d",
(int) $per_page,
$offset
);
$rows = $wpdb->get_results($sql, ARRAY_A);
return is_array($rows) ? $rows : array();
}
private function fetch_price_rows($per_page, $page) {
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_role_prices';
if (!aieo_dmm_table_exists($table)) {
return array();
}
$offset = max(0, ((int) $page - 1) * (int) $per_page);
$sql = $wpdb->prepare(
"SELECT * FROM {$table} ORDER BY product_id ASC LIMIT %d OFFSET %d",
(int) $per_page,
$offset
);
$rows = $wpdb->get_results($sql, ARRAY_A);
return is_array($rows) ? $rows : array();
}
private function create_job($job_id, $type, $total, array $params) {
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_export_jobs';
if (!aieo_dmm_table_exists($table)) {
return;
}
$wpdb->insert($table, array(
'job_id' => $job_id,
'type' => $type,
'status' => 'running',
'total' => $total,
'processed' => 0,
'params' => wp_json_encode($params),
'user_id' => get_current_user_id(),
), array('%s', '%s', '%s', '%d', '%d', '%s', '%d'));
}
private function load_job($job_id) {
global $wpdb;
if ($job_id === '') {
return null;
}
$table = $wpdb->prefix . 'aieo_dmm_export_jobs';
$row = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$table} WHERE job_id = %s",
$job_id
), ARRAY_A);
return is_array($row) ? $row : null;
}
private function update_job($job_id, array $fields) {
global $wpdb;
$table = $wpdb->prefix . 'aieo_dmm_export_jobs';
$formats = array();
foreach ($fields as $k => $v) {
$formats[] = in_array($k, array('processed', 'total', 'last_id', 'user_id'), true) ? '%d' : '%s';
}
$wpdb->update($table, $fields, array('job_id' => $job_id), $formats, array('%s'));
}
public function ajax_gf_export_forms() {
AIEO_DMM::verify_ajax_request();
if (!class_exists('GFAPI') || !class_exists('GFExport')) {
wp_send_json_error(array('message' => __('Gravity Forms is not active on this site.', 'ai-eshop-optimizer')));
}
$ids = array();
foreach (array('gf_registration_form_id', 'gf_update_form_id', 'gf_prompt_form_id') as $k) {
$id = (int) AIEO_DMM_Settings::get($k, 0);
if ($id > 0) { $ids[$id] = true; }
}
$ids = array_keys($ids);
if (empty($ids)) {
wp_send_json_error(array('message' => __('Configure at least one form ID first.', 'ai-eshop-optimizer')));
}
$forms = array();
foreach ($ids as $id) {
$form = GFAPI::get_form((int) $id);
if (is_array($form)) {
$forms[] = $form;
}
}
if (empty($forms)) {
wp_send_json_error(array('message' => __('Could not resolve any of the configured forms.', 'ai-eshop-optimizer')));
}
$payload = array();
$payload['version'] = defined('GFForms::$version') ? GFForms::$version : '2.7';
$payload['aieo_export_meta'] = array(
'exported_at' => gmdate('c'),
'gf_registration_form_id' => (int) AIEO_DMM_Settings::get('gf_registration_form_id', 0),
'gf_update_form_id' => (int) AIEO_DMM_Settings::get('gf_update_form_id', 0),
'gf_prompt_form_id' => (int) AIEO_DMM_Settings::get('gf_prompt_form_id', 0),
'gf_checkbox_field_id' => (int) AIEO_DMM_Settings::get('gf_checkbox_field_id', 11),
'gf_role_map' => (array) AIEO_DMM_Settings::get('gf_role_map', array()),
);
foreach ($forms as $i => $form) {
$payload[(string) $i] = $form;
}
$dir = AIEO_DMM::get_upload_path('exports');
$filename = 'gf-membership-forms-' . gmdate('Ymd-His') . '.json';
$path = trailingslashit($dir['path']) . $filename;
$url = trailingslashit($dir['url']) . $filename;
$written = file_put_contents($path, wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
if ($written === false) {
wp_send_json_error(array('message' => __('Could not write export file.', 'ai-eshop-optimizer')));
}
wp_send_json_success(array(
'url' => $url,
'filename' => $filename,
'forms' => count($forms),
));
}
private function gf_inverted_role_map() {
$map = (array) AIEO_DMM_Settings::get('gf_role_map', array());
$out = array();
foreach ($map as $slug => $value) {
$int_val = (int) $value;
if ($int_val > 0 && (string) $slug !== '') {
$out[$int_val] = (string) $slug;
}
}
return $out;
}
private function gf_collected_checkbox_values($entry) {
$field_id = (int) AIEO_DMM_Settings::get('gf_checkbox_field_id', 11);
if ($field_id <= 0 || !is_array($entry)) {
return array();
}
$out = array();
for ($i = 1; $i <= 30; $i++) {
$key = $field_id . '.' . $i;
if (!isset($entry[$key])) {
continue;
}
$raw = trim((string) $entry[$key]);
if ($raw === '') {
continue;
}
$iv = (int) $raw;
if ($iv > 0) {
$out[] = $iv;
}
}
return $out;
}
public function gf_assign_roles_on_registration($user_id, $user_config, $entry) {
$form_id = (int) AIEO_DMM_Settings::get('gf_registration_form_id', 0);
if ($form_id <= 0) { return; }
if (!is_array($entry) || (int) (isset($entry['form_id']) ? $entry['form_id'] : 0) !== $form_id) {
return;
}
$user = get_userdata($user_id);
if (!$user) { return; }
$inv = $this->gf_inverted_role_map();
if (empty($inv)) { return; }
foreach ($this->gf_collected_checkbox_values($entry) as $val) {
if (isset($inv[$val])) {
AIEO_Safe_Roles::grant((int) $user_id, $inv[$val], 'gravity_forms');
}
}
}
public function gf_modify_roles_on_update($entry, $form) {
$form_id = (int) AIEO_DMM_Settings::get('gf_update_form_id', 0);
if ($form_id <= 0) { return; }
if (!is_array($form) || (int) $form['id'] !== $form_id) { return; }
$user = wp_get_current_user();
if (!$user || !$user->ID) { return; }
$inv = $this->gf_inverted_role_map();
if (empty($inv)) { return; }
$chosen = array();
foreach ($this->gf_collected_checkbox_values($entry) as $val) {
if (isset($inv[$val])) {
$chosen[] = $inv[$val];
AIEO_Safe_Roles::grant((int) $user->ID, $inv[$val], 'gravity_forms');
}
}
foreach ($inv as $existing_role) {
if (!in_array($existing_role, $chosen, true)) {
AIEO_Safe_Roles::revoke((int) $user->ID, $existing_role, 'gravity_forms');
}
}
delete_transient('aieo_pre_gf_roles_' . $user->ID);
}
public function gf_emit_program_prompt($entry, $form) {
$form_id = (int) AIEO_DMM_Settings::get('gf_prompt_form_id', 0);
if ($form_id <= 0) { return; }
if (!is_array($form) || (int) $form['id'] !== $form_id) { return; }
$pro_field = (int) AIEO_DMM_Settings::get('gf_prompt_pro_field_id', 51);
$member_field = (int) AIEO_DMM_Settings::get('gf_prompt_member_field_id', 48);
$page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
if ($page_id <= 0) { return; }
$is_pro = (string) (isset($entry[$pro_field]) ? $entry[$pro_field] : '') === '1';
$not_in_prog = empty($entry[$member_field]) || (string) $entry[$member_field] === '0';
if (!$is_pro || !$not_in_prog) { return; }
$url = get_permalink($page_id);
if (!$url) { return; }
echo "';
}
public function gf_capture_pre_submission_roles() {
if (!is_user_logged_in()) { return; }
$page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
if ($page_id <= 0 || !is_page($page_id)) { return; }
$user = wp_get_current_user();
if (!$user || !$user->ID) { return; }
set_transient('aieo_pre_gf_roles_' . $user->ID, (array) $user->roles, 20000);
}
public function gf_print_post_login_refresh() {
$page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
if ($page_id <= 0 || !is_page($page_id)) { return; }
if (!isset($_GET['just_logged_in'])) { return; }
?>
post_name) || $page->post_name === '') { return; }
$uri = isset($_SERVER['REQUEST_URI']) ? (string) sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
if ($uri === '' || strpos($uri, '/' . $page->post_name) === false) { return; }
header('Cache-Control: no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
}
public function gf_membership_shortcode() {
if (class_exists('AIEO_Pro_Signup') && 'aieo' === AIEO_Pro_Signup::provider()) {
return AIEO_Pro_Signup::render('professional');
}
if (!function_exists('gravity_form')) {
return '';
}
$reg_form_id = (int) AIEO_DMM_Settings::get('gf_registration_form_id', 0);
$update_form_id = (int) AIEO_DMM_Settings::get('gf_update_form_id', 0);
if ($reg_form_id <= 0 && $update_form_id <= 0) {
return '';
}
if (is_user_logged_in()) {
if ($update_form_id <= 0) { return ''; }
$user = wp_get_current_user();
$map = (array) AIEO_DMM_Settings::get('gf_role_map', array());
$values = array();
foreach ((array) $user->roles as $role) {
if (isset($map[$role]) && (int) $map[$role] > 0) {
$values[] = (int) $map[$role];
}
}
$intro = (string) AIEO_DMM_Settings::get('gf_intro_logged_in', '');
$name = trim($user->first_name . ' ' . $user->last_name);
if ($name === '') { $name = $user->display_name; }
$intro = str_replace(
array('{user_name}', '{first_name}', '{last_name}'),
array(esc_html($name), esc_html($user->first_name), esc_html($user->last_name)),
$intro
);
ob_start();
if ($intro !== '') {
echo '' . wp_kses_post($intro) . '
';
}
echo gravity_form((int) $update_form_id, false, true, false, array(
'customer_email' => $user->user_email,
'user_id' => $user->ID,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'user_name' => $user->user_login,
'nick_name' => $user->nickname,
'membership' => implode(',', $values),
), true, 1, false);
return ob_get_clean();
}
if ($reg_form_id <= 0) {
}
$intro = (string) AIEO_DMM_Settings::get('gf_intro_logged_out', '');
$page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
$return_url = $page_id > 0 ? get_permalink($page_id) : home_url();
if ($return_url) {
$return_url = add_query_arg(array(
'just_logged_in' => 'true',
'ts' => time(),
), $return_url);
}
$intro = str_replace('{return_url}', esc_url($return_url), $intro);
ob_start();
if ($intro !== '') {
echo '' . wp_kses_post($intro) . '
';
}
if ($reg_form_id > 0) {
echo gravity_form((int) $reg_form_id, false, true, false, null, true, 1, false);
}
return ob_get_clean();
}
}