add_meta_data( self::ITEM_META, $token, true ); if ( $sides ) { $item->add_meta_data( __( 'Print', 'printblob' ), Cart::sides_label( array_keys( $sides ) ), true ); } } /** * @param int $item_id Order item ID. * @param \WC_Order_Item $item Order item. * @param int $order_id Order ID. */ public static function link_design_to_order( $item_id, $item, $order_id ): void { if ( ! $item instanceof \WC_Order_Item_Product ) { return; } $design = self::design_for_item( $item ); if ( $design ) { Design_Store::mark_ordered( $design->ID, (int) $order_id ); Review_Request::order_with_design(); } } /** The design reference is internal; the order screen shows the design itself. */ public static function hide_item_meta( array $keys ): array { $keys[] = self::ITEM_META; return $keys; } public static function design_for_item( \WC_Order_Item_Product $item ): ?\WP_Post { $token = (string) $item->get_meta( self::ITEM_META ); return $token ? Design_Store::find_by_token( $token ) : null; } /** * The design of an item an order screen shows: every design of the item's order is looked up with its first item. * * @param \WC_Order_Item_Product $item The item. */ private static function shown_design( \WC_Order_Item_Product $item ): ?\WP_Post { static $by_order = array(); $token = (string) $item->get_meta( self::ITEM_META ); if ( '' === $token ) { return null; } $order_id = $item->get_order_id(); if ( ! isset( $by_order[ $order_id ] ) ) { $order = $item->get_order(); $by_order[ $order_id ] = $order instanceof \WC_Order ? Design_Store::designs_by_token( Design_Store::order_tokens( $order ) ) : array(); } return $by_order[ $order_id ][ $token ] ?? null; } /** Whether the screen shown is WooCommerce's order edit screen, with or without its custom order tables. */ public static function on_edit_screen(): bool { return class_exists( OrderUtil::class ) && OrderUtil::is_order_edit_screen(); } /** * @param int $item_id Order item ID. * @param \WC_Order_Item $item Order item. */ public static function render_admin_design( $item_id, $item ): void { if ( ! $item instanceof \WC_Order_Item_Product || ! current_user_can( 'edit_shop_orders' ) ) { return; } $design = self::shown_design( $item ); if ( ! $design ) { return; } echo '
'; foreach ( Design_Store::sides( $design->ID ) as $side => $data ) { echo '
'; echo wp_get_attachment_image( (int) $data['preview_id'], 'thumbnail', false, array( 'class' => 'printblob-order-design__preview' ) ); echo '
'; self::side_links( $design->ID, (string) $side, (int) $item_id ); echo '
'; echo '
'; } /** * Fires after a designed item's previews and print file links on the order screen. * * @param \WC_Order_Item_Product $item Order item. * @param int $design_id Design ID. */ do_action( 'printblob_order_item_design', $item, $design->ID ); echo '
'; static $offered = false; if ( ! $offered ) { $offered = true; $order = $item->get_order(); if ( $order instanceof \WC_Order && self::pro_hint_due() ) { echo wp_kses_post( self::pro_hint( Design_Store::print_file_count( $order ) ) ); } } } /** What another plugin's links under a designed side may hold (printblob_order_design_links): a link, with an icon. */ public const LINKS_HTML = array( 'a' => array( 'href' => true, 'download' => true, 'class' => true, ), 'span' => array( 'class' => true, 'aria-hidden' => true, ), ); /** * A designed side's downloads on the order screen: its preview; its print file where one was made (orders placed * before PrintBlob 1.4.0, or by PrintBlob Pro); what other plugins add (printblob_order_design_links); and, while * Pro is not active, its print file and the customer's original images as the PrintBlob Pro add-on's controls, or, * where Pro is installed without a licence that lets it run, that no print file was made and why. * * @param int $design_id The design. * @param string $side One of its sides. * @param int $item_id The order item. * @param bool|null $pro_active Whether Pro's features run; Settings::pro_active() when null. * @param bool|null $pro_installed Whether Pro is installed; Settings::pro_installed() when null. */ public static function side_links( int $design_id, string $side, int $item_id, ?bool $pro_active = null, ?bool $pro_installed = null ): void { $pro_active = $pro_active ?? Settings::pro_active(); $locked = ! $pro_active && ! ( $pro_installed ?? Settings::pro_installed() ); $data = Design_Store::sides( $design_id )[ $side ] ?? array(); $label = Product_Settings::side_label( $side ); $preview = wp_get_attachment_url( (int) ( $data['preview_id'] ?? 0 ) ); echo ''; } /** * A designed side's downloads as side_links() prints them. * * @param int $design_id The design. * @param string $side One of its sides. * @param int $item_id The order item. * @param bool|null $pro_active Whether Pro's features run; Settings::pro_active() when null. * @param bool|null $pro_installed Whether Pro is installed; Settings::pro_installed() when null. */ public static function side_links_html( int $design_id, string $side, int $item_id, ?bool $pro_active = null, ?bool $pro_installed = null ): string { ob_start(); self::side_links( $design_id, $side, $item_id, $pro_active, $pro_installed ); return (string) ob_get_clean(); } /** * One quiet line under an order's first designed item about the PrintBlob Pro add-on's ZIP of its print files, while * Pro is not active and until this admin dismisses it. * * @param int $files The order's print files. * @param bool|null $pro_active Whether Pro is active; Settings::pro_active() when null. * @param int|null $user_id The admin; the current user when null. */ public static function pro_hint( int $files, ?bool $pro_active = null, ?int $user_id = null ): string { if ( $files < 1 || ! self::pro_hint_due( $pro_active, $user_id ) ) { return ''; } return sprintf( '

%1$s %3$s · %5$s

', /* translators: %d: number of print files of the order. */ esc_html( sprintf( _n( 'This order’s %d print file in one ZIP, with PrintBlob Pro.', 'This order’s %d print files in one ZIP, with PrintBlob Pro.', $files, 'printblob' ), $files ) ), esc_url( Pro::checkout_url( 'order-zip' ) ), esc_html__( 'About the PrintBlob Pro add-on', 'printblob' ), esc_url( Admin_Menu::action_url( 'printblob_dismiss_pro_hint' ) ), esc_html__( 'Dismiss', 'printblob' ) ); } /** * Whether the PrintBlob Pro line may show to this admin: Pro is not active and they have not dismissed it. * * @param bool|null $pro_active Whether Pro is active; Settings::pro_active() when null. * @param int|null $user_id The admin; the current user when null. */ private static function pro_hint_due( ?bool $pro_active = null, ?int $user_id = null ): bool { return ! ( $pro_active ?? Settings::pro_active() ) && ! get_user_meta( $user_id ?? get_current_user_id(), self::PRO_HINT_DISMISSED, true ); } public static function dismiss_pro_hint(): void { Admin_Menu::guard( 'printblob_dismiss_pro_hint', 'edit_shop_orders' ); update_user_meta( get_current_user_id(), self::PRO_HINT_DISMISSED, 1 ); Admin_Menu::back(); } /** * @param string $image Thumbnail HTML. * @param \WC_Order_Item $item Order item. */ public static function order_item_thumbnail( $image, $item ) { if ( ! $item instanceof \WC_Order_Item_Product ) { return $image; } $design = self::shown_design( $item ); if ( ! $design ) { return $image; } $sides = Design_Store::sides( $design->ID ); $first = reset( $sides ); $html = $first ? wp_get_attachment_image( (int) $first['preview_id'], 'woocommerce_thumbnail', false, array( 'alt' => __( 'Design', 'printblob' ) ) ) : ''; return $html ? $html : $image; } }