esc_html( $title ), 'url' => esc_url_raw( (string) $url ), 'new_tab' => false, 'icon' => '', 'image' => esc_url_raw( (string) $image ), 'author' => '', ); } }; $count = (int) $source['count']; if ( 'posts' === $source['type'] ) { $args = array( 'numberposts' => $count, 'post_status' => 'publish', 'suppress_filters' => false, ); if ( '' !== $source['term'] ) { list( $taxonomy, $term_id ) = explode( ':', $source['term'] ); $args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- a few posts, cached. array( 'taxonomy' => $taxonomy, 'terms' => (int) $term_id, ), ); } foreach ( get_posts( $args ) as $post ) { $add( get_the_title( $post ), get_permalink( $post ), $source['images'] ? get_the_post_thumbnail_url( $post, 'thumbnail' ) : '' ); } } elseif ( 'products' === $source['type'] && function_exists( 'wc_get_products' ) ) { $args = array( 'limit' => $count, 'status' => 'publish', 'visibility' => 'catalog', ); $chosen = 'chosen' === $source['products']; if ( $chosen ) { // The products the admin picked, in that order (also hidden ones: the admin chose them). $args = array( 'limit' => count( $source['ids'] ), 'status' => 'publish', 'include' => $source['ids'] ? $source['ids'] : array( 0 ), ); } switch ( $source['products'] ) { case 'on_sale': $args['include'] = array_merge( array( 0 ), wc_get_product_ids_on_sale() ); break; case 'featured': $args['featured'] = true; break; case 'best_selling': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'total_sales'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- a few products, cached. $args['order'] = 'DESC'; break; } if ( '' !== $source['term'] && ! $chosen ) { $term = get_term( (int) explode( ':', $source['term'] )[1] ); if ( $term && ! is_wp_error( $term ) ) { $args['category'] = array( $term->slug ); } } $products = wc_get_products( $args ); if ( $chosen ) { $order = array_flip( $source['ids'] ); usort( $products, function ( $a, $b ) use ( $order ) { return $order[ $a->get_id() ] - $order[ $b->get_id() ]; } ); } foreach ( $products as $product ) { $price = $source['prices'] && '' !== $product->get_price() ? wp_strip_all_tags( wc_price( wc_get_price_to_display( $product ) ) ) : ''; if ( '' !== $source['text'] ) { // Own text around the product: "🔥 {product} for only {price}". $text = '' === $price ? runline_drop_var( $source['text'], 'price' ) : $source['text']; $title = strtr( $text, array( '{product}' => $product->get_name(), '{price}' => $price, ) ); } else { $title = $product->get_name() . ( '' !== $price ? ' — ' . $price : '' ); } $image = $source['images'] ? wp_get_attachment_image_url( $product->get_image_id(), 'thumbnail' ) : ''; $add( $title, $product->get_permalink(), $image ); } } elseif ( 'orders' === $source['type'] && function_exists( 'wc_get_orders' ) ) { $orders = wc_get_orders( array( 'limit' => $count, 'status' => array( 'wc-processing', 'wc-completed' ), 'date_created' => '>' . ( time() - (int) $source['days'] * DAY_IN_SECONDS ), 'orderby' => 'date', 'order' => 'DESC', 'type' => 'shop_order', ) ); foreach ( $orders as $order ) { $lines = $order->get_items(); $line = $lines ? reset( $lines ) : null; if ( ! $line ) { continue; } $product = $line->get_product(); // Personal data as little as possible: the first letter of the name by default, the city only when asked for. $city = $source['city'] ? trim( $order->get_billing_city() ) : ''; $template = $source['template']; if ( '' !== $city && runline_default_settings()['source']['template'] === $template ) { $template = __( '{name} from {city} bought {product} {time}', 'runline' ); } if ( '' === $city ) { $template = runline_drop_var( $template, 'city' ); } $first = trim( $order->get_billing_first_name() ); $name = __( 'Someone', 'runline' ); if ( '' !== $first && 'first' === $source['name'] ) { $name = $first; } elseif ( '' !== $first && 'initial' === $source['name'] ) { $name = mb_strtoupper( mb_substr( $first, 0, 1 ) ) . '.'; } $title = strtr( $template, array( '{name}' => $name, '{city}' => $city, '{product}' => $line->get_name(), /* translators: %s: time, e.g. "5 minutes". */ '{time}' => sprintf( __( '%s ago', 'runline' ), human_time_diff( $order->get_date_created()->getTimestamp() ) ), ) ); $image = $source['images'] && $product ? wp_get_attachment_image_url( $product->get_image_id(), 'thumbnail' ) : ''; $add( $title, $product ? $product->get_permalink() : '', $image ); } } elseif ( 'rss' === $source['type'] && '' !== $source['rss'] ) { include_once ABSPATH . WPINC . '/feed.php'; $feed = fetch_feed( $source['rss'] ); if ( ! is_wp_error( $feed ) ) { foreach ( $feed->get_items( 0, $count ) as $entry ) { $add( $entry->get_title(), $entry->get_permalink() ); } } } return apply_filters( 'runline_source_items', $items, $source ); } /** * Removes a variable that has no value from a text, together with the word before it ("from {city}", "for {price}"). * * @param string $text Text. * @param string $name Variable name without braces. */ function runline_drop_var( $text, $name ) { return trim( preg_replace( '/\s*\S*\s*\{' . preg_quote( $name, '/' ) . '\}/u', '', $text ) ); } /** * Values of the text variables. {free_shipping_left} and {coupon} are made by the bar itself. */ function runline_text_vars() { return array( '{date}' => esc_html( date_i18n( get_option( 'date_format' ) ) ), '{site_name}' => esc_html( get_bloginfo( 'name' ) ), ); } /** * Free shipping threshold: the setting, or the smallest "minimum order amount" of WooCommerce free shipping. * * @param array $settings Bar settings. * @return float 0 = unknown. */ function runline_free_shipping_threshold( $settings ) { if ( $settings['shipping']['threshold'] > 0 ) { return (float) $settings['shipping']['threshold']; } if ( ! class_exists( 'WC_Shipping_Zones' ) ) { return 0; } $found = get_transient( 'runline_free_shipping_min' ); if ( false === $found ) { $found = 0; $zones = WC_Shipping_Zones::get_zones(); $zones[] = array( 'zone_id' => 0 ); foreach ( $zones as $zone ) { foreach ( WC_Shipping_Zones::get_zone( $zone['zone_id'] )->get_shipping_methods( true ) as $method ) { $min = 'free_shipping' === $method->id ? (float) $method->get_option( 'min_amount' ) : 0; if ( $min > 0 && ( ! $found || $min < $found ) ) { $found = $min; } } } set_transient( 'runline_free_shipping_min', $found, HOUR_IN_SECONDS ); } return (float) $found; } /** * End of the sale of a product (Unix time, 0 = none). * * @param int $product_id Product ID. */ function runline_sale_end( $product_id ) { if ( ! $product_id || ! function_exists( 'wc_get_product' ) ) { return 0; } $product = wc_get_product( $product_id ); $date = $product ? $product->get_date_on_sale_to() : null; return $date ? $date->getTimestamp() : 0; } /** * Price in the format of the shop, as plain text (for the browser: free shipping progress). */ function runline_price_format() { if ( ! function_exists( 'get_woocommerce_currency_symbol' ) ) { global $wp_locale; return array( 'symbol' => '', 'format' => '%2$s', 'decimals' => 2, 'dec' => $wp_locale ? $wp_locale->number_format['decimal_point'] : '.', 'thou' => $wp_locale ? $wp_locale->number_format['thousands_sep'] : ',', ); } return array( 'symbol' => html_entity_decode( get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8' ), 'format' => html_entity_decode( get_woocommerce_price_format(), ENT_QUOTES, 'UTF-8' ), 'decimals' => wc_get_price_decimals(), 'dec' => wc_get_price_decimal_separator(), 'thou' => wc_get_price_thousand_separator(), ); } // Refresh the free shipping threshold when shipping settings change. add_action( 'woocommerce_update_options_shipping', function () { delete_transient( 'runline_free_shipping_min' ); } );