$plan, 'from' => $from, ), FACTS['get_pro'] ); } /** * printblob.com's live demo, opened on the 3D view. * * @param string $from Where the link is, one of FACTS['placements']. */ public static function demo_url( string $from ): string { return add_query_arg( 'from', $from, FACTS['demo_3d'] ); } /** * Pro's features and services, and the free plugin's features, in this site's language (includes/pro-words.php, * written from printblob.com's plans), read once a request. * * @return array{features:array, services:string[], free:array, sleeves:array} */ public static function words(): array { static $words = null; if ( null === $words ) { $words = require __DIR__ . '/pro-words.php'; } return $words; } /** A service, which reads inside a sentence on printblob.com, as a list item: its first letter capitalised, in any script. */ public static function capitalised( string $text ): string { return function_exists( 'mb_strtoupper' ) ? mb_strtoupper( mb_substr( $text, 0, 1 ) ) . mb_substr( $text, 1 ) : ucfirst( $text ); } public static function compare_url(): string { return admin_url( 'admin.php?page=' . self::PAGE ); } public static function offer_text(): string { return __( 'Get PrintBlob Pro', 'printblob' ); } public static function prices_text(): string { /* translators: 1: PrintBlob Pro's yearly price, with its currency sign, 2: its one-time price, with its currency sign. */ return sprintf( __( '%1$s a year or %2$s once', 'printblob' ), FACTS['pro']['yearly'], FACTS['pro']['lifetime'] ); } public static function refund_text(): string { $days = (int) FACTS['pro']['refund_days']; /* translators: %d: days in which a PrintBlob Pro purchase is refunded in full. */ return sprintf( _n( 'Full refund within %d day.', 'Full refund within %d days.', $days, 'printblob' ), $days ); } public static function separate_text(): string { return __( 'PrintBlob Pro is a separate plugin from printblob.com. Everything PrintBlob does works without it.', 'printblob' ); } /** * What Pro adds, one line each: its features, then what a licence brings. * * @return string[] */ public static function points(): array { return array_merge( array_map( static fn( array $feature ): string => (string) $feature['point'], self::words()['features'] ), array_map( array( __CLASS__, 'capitalised' ), self::words()['services'] ) ); } /** What Pro adds (points()), as list items. */ public static function points_html(): string { return implode( '', array_map( static fn( string $point ): string => '
  • ' . esc_html( $point ) . '
  • ', self::points() ) ); } /** * "Get PrintBlob Pro" and "See it in 3D". * * @param string $from Where the buttons are, one of FACTS['placements']. */ public static function buttons_html( string $from ): string { return sprintf( '%2$s %4$s', esc_url( self::checkout_url( $from ) ), esc_html( self::offer_text() ), esc_url( self::demo_url( $from ) ), esc_html__( 'See it in 3D', 'printblob' ) ); } /** * The overview's PrintBlob Pro card while Pro is not active: in full until this admin folds it, then one line. * * @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 card_html( ?bool $pro_active = null, ?int $user_id = null ): string { if ( $pro_active ?? Settings::pro_active() ) { return ''; } if ( get_user_meta( $user_id ?? get_current_user_id(), self::CARD_DISMISSED, true ) ) { return sprintf( '

    %1$s %2$s · %3$s · %5$s · %7$s

    ', esc_html__( 'PrintBlob Pro add-on:', 'printblob' ), esc_html( self::prices_text() ), esc_html( self::refund_text() ), esc_url( self::checkout_url( 'overview-line' ) ), esc_html( self::offer_text() ), esc_url( self::compare_url() ), esc_html__( 'Compare free and Pro', 'printblob' ) ); } return sprintf( '

    %1$s

    %12$s

    %2$s

    %3$s
    %4$s

      %5$s

    %6$s %8$s

    %9$s

    %11$s

    ', esc_html__( 'PrintBlob Pro add-on', 'printblob' ), esc_html( self::offer_text() ), esc_html( self::prices_text() ), esc_html( self::refund_text() ), self::points_html(), self::buttons_html( 'overview-card' ), esc_url( self::compare_url() ), esc_html__( 'Compare free and Pro', 'printblob' ), esc_html( self::separate_text() ), esc_url( Admin_Menu::action_url( 'printblob_dismiss_pro_card' ) ), esc_html__( 'Show less', 'printblob' ), self::still_html() ); } /** The 3D still, assets/admin/pro-3d.webp: a product in PrintBlob Pro's 3D view, captured from the demo store. */ public const STILL = 'assets/admin/pro-3d.webp'; /** The 3D still at its own size, or nothing while the plugin has no such file. */ public static function still_html(): string { static $size = null; if ( null === $size ) { $file = dirname( FILE ) . '/' . self::STILL; $size = is_readable( $file ) ? wp_getimagesize( $file ) : false; } if ( ! $size ) { return ''; } return sprintf( '%4$s', esc_url( plugins_url( self::STILL, FILE ) ), (int) $size[0], (int) $size[1], esc_attr__( 'A product with a printed design in PrintBlob Pro’s 3D view', 'printblob' ) ); } /** * The designer's "3D · Pro" link, for a shop manager seeing a product page while PrintBlob Pro is not active; null * for everyone else, customers included. The page it is on is then a logged-in user's, which page caches skip. * * @param bool|null $pro_active Whether Pro is active; Settings::pro_active() when null. * @return array{label:string, title:string, href:string}|null */ public static function storefront_chip( ?bool $pro_active = null ): ?array { if ( ( $pro_active ?? Settings::pro_active() ) || ! is_user_logged_in() || ! current_user_can( Admin_Menu::CAPABILITY ) ) { return null; } return array( 'label' => __( '3D · Pro', 'printblob' ), 'title' => __( 'Only you see this, as the shop manager: your product in 3D with the PrintBlob Pro add-on, on printblob.com', 'printblob' ), 'href' => self::demo_url( 'storefront-3d' ), ); } public static function dismiss_card(): void { Admin_Menu::guard( 'printblob_dismiss_pro_card' ); update_user_meta( get_current_user_id(), self::CARD_DISMISSED, 1 ); Admin_Menu::back( Admin_Menu::url() ); } /** * What the free plugin does (printblob.com's plans, free.features), each in both columns of the comparison. * * @return string[] */ public static function free_points(): array { return array_map( static fn( array $feature ): string => (string) $feature['point'], self::words()['free'] ); } /** * The comparison's rows: what both have, then what Pro adds. * * @return array */ public static function comparison(): array { return array_merge( array_map( static fn( string $text ): array => array( 'text' => $text, 'free' => true, ), self::free_points() ), array_map( static fn( string $text ): array => array( 'text' => $text, 'free' => false, ), self::points() ) ); } public static function compare_page(): void { Admin_Menu::allow(); $mark = static fn( bool $yes ): string => Admin_Menu::mark_html( $yes, $yes ? __( 'Included', 'printblob' ) : __( 'Not included', 'printblob' ), false ); ?> '3d', 'product-back' => 'back', 'product-sleeves' => 'sleeves', 'settings-fonts' => 'fonts', 'queue-zip' => 'print-queue', 'order-print' => 'print-files', 'order-originals' => 'print-files', 'queue-print' => 'print-files', ); /** * A feature of Pro from printblob.com's plans. * * @param string $id The feature's id. * @return array{id:string, name:string, point:string} */ public static function feature( string $id ): array { foreach ( self::words()['features'] as $feature ) { if ( $feature['id'] === $id ) { return $feature; } } throw new \InvalidArgumentException( esc_html( "PrintBlob Pro has no feature {$id}." ) ); } /** * A switched-off Pro control: a button labelled as the PrintBlob Pro add-on that opens the upgrade box. * * @param string $from Its placement, a key of LOCKS. * @param string $label The button's words; "PrintBlob Pro add-on" when empty. */ public static function lock( string $from, string $label = '' ): void { $lock = self::feature( self::LOCKS[ $from ] ); ?>

    $pro_sides ) { foreach ( array_diff( $pro_sides, $sides ) as $side ) { $id = 'printblob_pro_price_' . $side; /* translators: %s: product side, e.g. Front, Back or Left sleeve. */ $label = sprintf( __( 'Print price: %s', 'printblob' ), self::words()['sleeves'][ $side ] ?? Product_Settings::side_label( $side ) ); ?>