__( 'Catalogue mode', 'ai-eshop-optimizer' ),
'check' => __( 'Show this category as a catalogue', 'ai-eshop-optimizer' ),
'help' => __( 'Its products, and its sub-categories\' products, show no price and cannot be added to the cart; with Product Requests on, their cards and pages offer "Request a quote" instead. A product may say otherwise on its own screen.', 'ai-eshop-optimizer' ),
);
}
public function render_term_add() {
$w = self::term_words();
echo '
';
woocommerce_wp_select(
array(
'id' => AIEO_Catalogue_Mode::META_PRODUCT,
'label' => __( 'Catalogue mode', 'ai-eshop-optimizer' ),
'description' => __( 'A catalogue product shows no price and cannot be added to the cart; with Product Requests on, its card and page offer "Request a quote". "As its categories" follows a category shown as a catalogue and the shop\'s switches.', 'ai-eshop-optimizer' ),
'desc_tip' => true,
'value' => (string) get_post_meta( (int) $post->ID, AIEO_Catalogue_Mode::META_PRODUCT, true ),
'options' => array(
'' => __( 'As its categories and the shop', 'ai-eshop-optimizer' ),
'yes' => __( 'A catalogue product: no price, no add to cart', 'ai-eshop-optimizer' ),
'no' => __( 'On sale: its price and add to cart', 'ai-eshop-optimizer' ),
),
)
);
echo '
';
}
public function save_product_field( $post_id ) {
$v = isset( $_POST[ AIEO_Catalogue_Mode::META_PRODUCT ] ) ? sanitize_key( wp_unslash( (string) $_POST[ AIEO_Catalogue_Mode::META_PRODUCT ] ) ) : '';
if ( in_array( $v, array( 'yes', 'no' ), true ) ) {
update_post_meta( (int) $post_id, AIEO_Catalogue_Mode::META_PRODUCT, $v );
} else {
delete_post_meta( (int) $post_id, AIEO_Catalogue_Mode::META_PRODUCT );
}
self::bump();
}
private static function bump() {
if ( class_exists( 'AIEO_Catalogue_Mode' ) ) {
AIEO_Catalogue_Mode::flush();
}
if ( class_exists( 'AIEO_DMM_Cache' ) ) {
AIEO_DMM_Cache::bump_version( 'prices' );
}
}
public static function marked() {
$categories = get_terms( array( 'taxonomy' => 'product_cat', 'hide_empty' => false, 'meta_key' => AIEO_Catalogue_Mode::META_TERM, 'meta_value' => 'yes' ) );
$ids = static function ( $value ) {
return array_map( 'intval', get_posts( array( 'post_type' => 'product', 'post_status' => 'any', 'numberposts' => 50, 'fields' => 'ids', 'meta_key' => AIEO_Catalogue_Mode::META_PRODUCT, 'meta_value' => $value ) ) );
};
return array( 'categories' => is_array( $categories ) ? $categories : array(), 'catalogue' => $ids( 'yes' ), 'on_sale' => $ids( 'no' ) );
}
public function ajax_save() {
AIEO_DMM::verify_ajax_request();
if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Not saved: you do not manage the shop.', 'ai-eshop-optimizer' ) ) );
}
$words = array(
'price_text' => AIEO_Catalogue_Mode::STRING_NAME,
'guest_note' => AIEO_Catalogue_Mode::NOTE_NAME,
);
$save = array(
'hide_prices' => ! empty( $_POST['hide_prices'] ),
'disable_cart' => ! empty( $_POST['disable_cart'] ),
'viewers' => ! empty( $_POST['viewers'] ),
'card_words' => ! empty( $_POST['card_words'] ),
'guest_line' => ! empty( $_POST['guest_line'] ),
);
foreach ( $words as $key => $name ) {
$save[ $key ] = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( (string) $_POST[ $key ] ) ) : '';
}
AIEO_Catalogue_Mode::update( $save );
foreach ( $words as $key => $name ) {
if ( '' !== $save[ $key ] ) {
do_action( 'wpml_register_single_string', AIEO_Catalogue_Mode::STRINGS_DOMAIN, $name, $save[ $key ] );
if ( function_exists( 'pll_register_string' ) ) {
pll_register_string( $name, $save[ $key ], AIEO_Catalogue_Mode::STRINGS_DOMAIN );
}
}
}
if ( class_exists( 'AIEO_DMM_Cache' ) ) {
AIEO_DMM_Cache::bump_version( 'prices' );
}
do_action( 'aieo_purge_page_cache' );
if ( class_exists( 'AIEO_Recommendations_Cache' ) ) {
AIEO_Recommendations_Cache::flush_all_cache();
}
if ( class_exists( 'WC_Cache_Helper' ) ) {
WC_Cache_Helper::get_transient_version( 'product', true );
}
wp_send_json_success( array( 'message' => __( 'Saved.', 'ai-eshop-optimizer' ) ) );
}
public static function viewer_count() {
if ( null === get_role( AIEO_Catalogue_Mode::ROLE ) ) {
return 0;
}
$query = new WP_User_Query( array( 'role' => AIEO_Catalogue_Mode::ROLE, 'fields' => 'ID', 'number' => 1, 'count_total' => true ) );
return (int) $query->get_total();
}
public static function viewers_url( $holders = true ) {
$query = array( 'page' => AIEO_DMM::MENU_SLUG, 'dmm_tab' => 'user_roles' ) + ( $holders ? array( 'role' => AIEO_Catalogue_Mode::ROLE ) : array() );
return add_query_arg( $query, admin_url( 'admin.php' ) ) . '#aieo-card-user-roles-find';
}
public static function price_outlets() {
global $wpdb;
$out = array();
$count = static function ( $table ) use ( $wpdb ) {
$table = $wpdb->prefix . $table;
if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) ) ) !== $table ) {
return 0;
}
return (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}` WHERE enabled = 1" );
};
$tab = static function ( $id ) {
return add_query_arg( array( 'page' => AIEO_DMM::MENU_SLUG, 'dmm_tab' => $id ), admin_url( 'admin.php' ) );
};
$feeds = $count( 'aieo_dmm_feed_configs' );
if ( $feeds > 0 ) {
$out[] = array(
'text' => sprintf( _n( '%s marketplace feed is on and publishes every product\'s price (Google Shopping, Skroutz and the like read it). A shop that hides its prices publishes no priced feed: turn it off.', '%s marketplace feeds are on and publish every product\'s price (Google Shopping, Skroutz and the like read them). A shop that hides its prices publishes no priced feed: turn them off.', $feeds, 'ai-eshop-optimizer' ), number_format_i18n( $feeds ) ),
'url' => $tab( 'marketplace_feeds' ),
'link' => __( 'Marketplace Feeds', 'ai-eshop-optimizer' ),
);
}
$exchanged = $count( 'aieo_dmm_exchanger_feeds' );
if ( $exchanged > 0 ) {
$out[] = array(
'text' => sprintf( _n( '%s feed to another site is on and carries every product\'s price. Keep it only if the site that reads it hides prices too.', '%s feeds to other sites are on and carry every product\'s price. Keep them only if the sites that read them hide prices too.', $exchanged, 'ai-eshop-optimizer' ), number_format_i18n( $exchanged ) ),
'url' => $tab( 'product_exchanger' ),
'link' => __( 'Product feeds between sites', 'ai-eshop-optimizer' ),
);
}
$fibo = get_option( 'dgwt_wcas_settings', array() );
if ( function_exists( 'DGWT_WCAS' ) && is_array( $fibo ) && 'on' === ( $fibo['show_product_price'] ?? '' ) ) {
$out[] = array(
'text' => __( 'FiboSearch shows each product\'s price in its search suggestions. It answers them from its own index, the same for every visitor, so the switches here cannot hide them: turn off “Show price” in FiboSearch\'s settings.', 'ai-eshop-optimizer' ),
'url' => admin_url( 'admin.php?page=dgwt_wcas_settings' ),
'link' => __( 'FiboSearch\'s settings', 'ai-eshop-optimizer' ),
);
}
return $out;
}
public function render_subtab() {
$s = AIEO_Catalogue_Mode::settings();
$form = 'aieo-card-catalogue-mode-form';
list( $open, $close ) = AIEO_DMM_UI::card(
array(
'anchor' => 'catalogue-mode-switches',
'icon' => 'book-open',
'title' => __( 'The shop as a catalogue', 'ai-eshop-optimizer' ),
'description' => __( 'The shop as a catalogue: on the storefront (product pages, shop and category pages, search, Rooster One\'s rails, sliders and side cart), prices hidden, adding to the cart turned off, or both. The admin, imports, ERP connections and feeds keep prices and orders as before.', 'ai-eshop-optimizer' ),
'footer' => \Rooster\Admin\States::save_button( $form ),
)
);
echo $open;
$rows = \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-hide-prices',
'label' => __( 'Hide prices', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::toggle( 'hide_prices', $s['hide_prices'], array( 'id' => 'aieo-cat-hide-prices', 'hidden' => false ) ),
'help' => __( 'No price anywhere on the storefront, nor in the products\' structured data.', 'ai-eshop-optimizer' ),
)
);
$rows .= \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-price-text',
'label' => __( 'In place of the price', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::input( 'price_text', $s['price_text'], array( 'id' => 'aieo-cat-price-text', 'medium' => true, 'placeholder' => __( 'Ask for a price', 'ai-eshop-optimizer' ) ) ),
'help' => __( 'Empty: nothing. With WPML or Polylang, translate it under String Translation (Rooster One).', 'ai-eshop-optimizer' ),
)
);
$rows .= \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-card-words',
'label' => __( 'On product cards too', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::toggle( 'card_words', $s['card_words'], array( 'id' => 'aieo-cat-card-words', 'hidden' => false ) ),
'help' => __( 'On: every product card in a list carries these words, and the guests\' sign-in line under them. Off: a card has nothing in its price\'s place, and the words stand on the product\'s own page alone.', 'ai-eshop-optimizer' ),
)
);
$rows .= \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-disable-cart',
'label' => __( 'Turn off add to cart', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::toggle( 'disable_cart', $s['disable_cart'], array( 'id' => 'aieo-cat-disable-cart', 'hidden' => false ) ),
'help' => __( 'No product can be added to the cart, and the add-to-cart buttons are not shown. With Product Requests on and its rule "products that cannot be bought" (its default), every product page shows the request form instead.', 'ai-eshop-optimizer' ),
)
);
$holders = self::viewer_count();
$held = $holders > 0 ? sprintf( _n( '%s account holds the role.', '%s accounts hold the role.', $holders, 'ai-eshop-optimizer' ), number_format_i18n( $holders ) ) : __( 'No account holds the role yet.', 'ai-eshop-optimizer' );
$rows .= \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-viewers',
'label' => __( 'Price viewers', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::toggle( 'viewers', $s['viewers'], array( 'id' => 'aieo-cat-viewers', 'hidden' => false ) ),
'help_html' => esc_html__( 'A signed-in account that holds the role “Price viewer” sees its own prices and buys as in an ordinary shop; every other visitor, signed in or not, keeps the catalogue. A category or a product marked as a catalogue stays one for a price viewer too. The role is given by hand, one account at a time, by an administrator or by staff an administrator lets give it; nothing gives it by itself.', 'ai-eshop-optimizer' )
. ' ' . esc_html( $held ) . ' ' . esc_html( $holders > 0 ? __( 'See them on the Roles screen', 'ai-eshop-optimizer' ) : __( 'Give it on the Roles screen', 'ai-eshop-optimizer' ) ) . '',
)
);
$rows .= \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-guest-line',
'label' => __( 'Tell guests to sign in', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::toggle( 'guest_line', $s['guest_line'], array( 'id' => 'aieo-cat-guest-line', 'hidden' => false ) ),
'help' => __( 'With Price viewers on, a visitor who is not signed in reads a line under the words in place of the price, linked to the sign-in page. Off: no such line.', 'ai-eshop-optimizer' ),
)
);
$rows .= \Rooster\Admin\Ui::field_row(
array(
'id' => 'aieo-cat-guest-note',
'label' => __( 'Sign-in line for guests', 'ai-eshop-optimizer' ),
'control' => \Rooster\Admin\Ui::input( 'guest_note', $s['guest_note'], array( 'id' => 'aieo-cat-guest-note', 'medium' => true, 'placeholder' => __( 'Log in to see your prices', 'ai-eshop-optimizer' ) ) ),
'help' => __( 'With Price viewers on, a visitor who is not signed in reads it under the words in place of the price, as a link to the sign-in page. Empty: “Log in to see your prices”. With WPML or Polylang, translate it under String Translation (Rooster One).', 'ai-eshop-optimizer' ),
)
);
echo AIEO_DMM_UI::card_form( 'aieo_dmm_catalogue_mode_save', 'catalogue_mode', $form, '
' . $rows . '
' );
foreach ( $s['hide_prices'] ? self::price_outlets() : array() as $outlet ) {
echo \Rooster\Admin\Ui::notice(
esc_html( $outlet['text'] ) . ' ' . esc_html( $outlet['link'] ) . '',
'warning',
array( 'inline' => true )
);
}
$requests = class_exists( 'AIEO_Product_Request_Settings' ) && AIEO_Product_Request_Settings::frontend_is_active();
echo \Rooster\Admin\Ui::notice(
esc_html( $requests ? __( 'Product Requests is on: visitors can ask about a product, or for a quote, where they cannot buy it.', 'ai-eshop-optimizer' ) : __( 'Product Requests is off: a catalogue without add to cart then offers no way to ask about a product. Turn it on in the Product page screen, or under Product Requests.', 'ai-eshop-optimizer' ) ),
$requests ? 'success' : 'info',
array( 'inline' => true )
);
if ( class_exists( 'AIEO_DMM_Settings' ) && 'catalog' === (string) AIEO_DMM_Settings::get( 'seo.site_profile', 'auto' ) ) {
$seo_url = add_query_arg( array( 'page' => AIEO_DMM::MENU_SLUG, 'dmm_tab' => 'seo_general' ), admin_url( 'admin.php' ) ) . '#aieo-card-general-general';
echo \Rooster\Admin\Ui::notice(
esc_html__( 'The site profile is Catalog: these switches were set with it, and choosing another profile turns them off.', 'ai-eshop-optimizer' ) . ' ' . esc_html__( 'The site profile', 'ai-eshop-optimizer' ) . '',
'info',
array( 'inline' => true )
);
}
echo $close;
$this->render_marked();
}
private function render_marked() {
$m = self::marked();
list( $open, $close ) = AIEO_DMM_UI::card(
array(
'anchor' => 'catalogue-mode-marked',
'icon' => 'folder-tree',
'title' => __( 'A catalogue within the shop', 'ai-eshop-optimizer' ),
'description' => __( 'Whatever the switches above say: a product category shown as a catalogue (its screen under Products → Categories) and a product marked a catalogue product (Product data → Advanced) show no price and cannot be added to the cart; their cards and pages offer "Request a quote" while Product Requests is on. A product marked on sale keeps its price and add to cart in a catalogue category, or in a catalogue shop.', 'ai-eshop-optimizer' ),
)
);
echo $open;
$link = static function ( $url, $text ) {
return '' . esc_html( $text ) . '';
};
$rows = array(
__( 'Categories shown as a catalogue', 'ai-eshop-optimizer' ) => array_map( static function ( $t ) use ( $link ) { return $link( get_edit_term_link( $t->term_id, 'product_cat' ), $t->name ); }, $m['categories'] ),
__( 'Catalogue products', 'ai-eshop-optimizer' ) => array_map( static function ( $id ) use ( $link ) { return $link( get_edit_post_link( $id, 'raw' ), get_the_title( $id ) ); }, $m['catalogue'] ),
__( 'Products on sale whatever the rest says', 'ai-eshop-optimizer' ) => array_map( static function ( $id ) use ( $link ) { return $link( get_edit_post_link( $id, 'raw' ), get_the_title( $id ) ); }, $m['on_sale'] ),
);
$html = '';
foreach ( $rows as $label => $links ) {
$html .= \Rooster\Admin\Ui::field_row(
array(
'group' => true,
'label' => $label,
'control' => $links ? '