' . esc_html__( 'Settings', 'eazign' ) . '' ); return $links; } /** * Styles and scripts of the Eazign pages. * * @param string $hook Page hook. */ public static function assets( $hook ) { if ( false === strpos( $hook, 'eazign' ) ) { return; } wp_enqueue_style( 'eazign-admin', EAZIGN_URL . 'assets/css/admin.css', array(), EAZIGN_VERSION ); wp_enqueue_script( 'eazign-admin', EAZIGN_URL . 'assets/js/admin.js', array(), EAZIGN_VERSION, true ); wp_localize_script( 'eazign-admin', 'eazignAdmin', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'eazign_sync' ), 'i18n' => array( 'working' => __( 'Creating the product… this takes a moment.', 'eazign' ), 'created' => __( 'Designer product created.', 'eazign' ), 'updated' => __( 'Designer product updated.', 'eazign' ), 'view' => __( 'View product', 'eazign' ), 'failed' => __( 'Failed:', 'eazign' ), /* translators: 1: current file, 2: number of files */ 'upload' => __( 'Uploading %1$d of %2$d…', 'eazign' ), 'noFiles' => __( 'Please choose a file.', 'eazign' ), /* translators: %d: number of variations */ 'one' => __( '1 product with %1$d variations', 'eazign' ), /* translators: 1: number of products, 2: number of variations */ 'summary' => __( '%1$d products with %2$d variations', 'eazign' ), /* translators: 1: done, 2: total */ 'running' => __( 'Creating products… %1$d of %2$d', 'eazign' ), 'done' => __( 'Done.', 'eazign' ), ), ) ); } // ---- notices ------------------------------------------------------------- /** * Stores a notice for the next page load. * * @param string $type success|error|warning. * @param string $message Text. */ public static function notice( $type, $message ) { set_transient( 'eazign_notice_' . get_current_user_id(), array( 'type' => $type, 'message' => $message, ), 60 ); } /** * Prints the stored notice. */ public static function show_notice() { $notice = get_transient( 'eazign_notice_' . get_current_user_id() ); if ( ! $notice ) { return; } delete_transient( 'eazign_notice_' . get_current_user_id() ); printf( '

%2$s

', esc_attr( $notice['type'] ), esc_html( $notice['message'] ) ); } /** * Error box for failed API calls. * * @param WP_Error $error Error. */ private static function error_box( $error ) { printf( '

%s

', esc_html( $error->get_error_message() ) ); } /** * Stops admin requests of users without the WooCommerce capability. */ private static function guard() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to do this.', 'eazign' ) ); } } // ---- connection ------------------------------------------------------------ /** * Connection page (start page of the plugin). */ public static function page_connection() { $connected = Eazign_Client::is_connected(); if ( $connected ) { // Keeps name, language and currency of the shop up to date. $status = Eazign_Client::post( 'status', array( 'name' => get_bloginfo( 'name' ), 'locale' => get_locale(), 'currency' => get_woocommerce_currency(), ) ); } ?>

Eazign

terms of service and the privacy policy of Eazign.', 'eazign' ) ), esc_url( $de ? 'https://eazign.at/wordpress/agb/' : 'https://eazign.at/en/wordpress/terms/' ), esc_url( $de ? 'https://eazign.at/wordpress/datenschutz/' : 'https://eazign.at/en/wordpress/privacy/' ) ); ?>

' . esc_html( Eazign_Client::site()['id'] ) . '' ); ?>

  1. blank: product data, colours, sizes, print sides and a base photo.', 'eazign' ), esc_url( admin_url( 'admin.php?page=eazign-blanks' ) ) ) ); ?>
  2. Print files.', 'eazign' ), esc_url( admin_url( 'admin.php?page=eazign-files' ) ) ) ); ?>

' . esc_html( $connected_url ) . '', '' . esc_html( home_url() ) . '' ); ?>

home_url() ) ); self::notice( is_wp_error( $result ) ? 'error' : 'success', is_wp_error( $result ) ? $result->get_error_message() : __( 'Shop address updated.', 'eazign' ) ); wp_safe_redirect( admin_url( 'admin.php?page=eazign' ) ); exit; } // ---- blanks ---------------------------------------------------------------- /** * Blank list or, with ?blank=, the blank editor. */ public static function page_blanks() { echo '
'; if ( ! Eazign_Client::is_connected() ) { echo '

' . esc_html__( 'Blanks', 'eazign' ) . '

'; echo '

' . wp_kses_post( sprintf( /* translators: %s: URL */ __( 'Please connect your shop first.', 'eazign' ), esc_url( admin_url( 'admin.php?page=eazign' ) ) ) ) . '

'; return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only navigation. $id = isset( $_GET['blank'] ) ? sanitize_key( wp_unslash( $_GET['blank'] ) ) : ''; if ( $id ) { self::blank_editor( $id ); } else { self::blank_list(); } echo ''; } /** * List of blanks. */ private static function blank_list() { $data = Eazign_Client::get( 'blanks' ); ?>


get_id() ) ) . '">' . esc_html( $product->get_name() ) . ''; } else { echo '–'; } ?>
' . esc_html__( 'Edit blank', 'eazign' ) . ''; echo '

← ' . esc_html__( 'All blanks', 'eazign' ) . '

'; if ( is_wp_error( $data ) ) { self::error_box( $data ); return; } $blank = $data['blank']; $techniques = self::techniques(); $custom = $blank['customSize']; $product = ! empty( $blank['productId'] ) ? wc_get_product( $blank['productId'] ) : null; ?>

$color ) : ?>

$view ) : ?>

get_id() ) ) . '">' . esc_html( $product->get_name() ) . '' ); ?> ·

label. */ private static function techniques() { $cached = get_transient( 'eazign_techniques' ); if ( is_array( $cached ) ) { return $cached; } $status = Eazign_Client::post( 'status', array() ); $list = array(); if ( ! is_wp_error( $status ) ) { foreach ( $status['techniques'] as $t ) { $list[ $t['id'] ] = $t['label']; } set_transient( 'eazign_techniques', $list, DAY_IN_SECONDS ); } return $list ? $list : array( 'dtf' => 'DTF' ); } /** * Add blank. */ public static function create_blank() { self::guard(); check_admin_referer( 'eazign_create_blank' ); $result = Eazign_Client::post( 'blanks', array( 'name' => '' ) ); if ( is_wp_error( $result ) ) { self::notice( 'error', $result->get_error_message() ); wp_safe_redirect( admin_url( 'admin.php?page=eazign-blanks' ) ); } else { wp_safe_redirect( admin_url( 'admin.php?page=eazign-blanks&blank=' . rawurlencode( $result['blank']['id'] ) ) ); } exit; } /** * Save blank (form above), then upload new base photos. */ public static function save_blank() { self::guard(); $id = isset( $_POST['blank'] ) ? sanitize_key( wp_unslash( $_POST['blank'] ) ) : ''; check_admin_referer( 'eazign_save_blank_' . $id ); $back = admin_url( 'admin.php?page=eazign-blanks&blank=' . rawurlencode( $id ) ); $current = Eazign_Client::get( 'blanks/' . rawurlencode( $id ) ); if ( is_wp_error( $current ) ) { self::notice( 'error', $current->get_error_message() ); wp_safe_redirect( $back ); exit; } $text = function ( $key ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- checked above. return isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : ''; }; $colors = array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- checked above, sanitized per field. foreach ( (array) ( isset( $_POST['colors'] ) ? wp_unslash( $_POST['colors'] ) : array() ) as $color ) { $name = sanitize_text_field( $color['name'] ?? '' ); if ( '' !== $name ) { $colors[] = array( 'name' => $name, 'hex' => sanitize_hex_color( $color['hex'] ?? '' ) ?? '#ffffff', ); } } $views = array(); $indices = array(); $sheets = array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- checked above, sanitized per field. foreach ( (array) ( isset( $_POST['views'] ) ? wp_unslash( $_POST['views'] ) : array() ) as $i => $view ) { if ( '__i__' === (string) $i ) { continue; } $remove = ! empty( $view['remove'] ); $views[] = array( 'id' => sanitize_key( $view['id'] ?? '' ), 'remove' => $remove, 'name' => sanitize_text_field( $view['name'] ?? '' ), 'printWidthMm' => (float) ( $view['printWidthMm'] ?? 0 ), 'printHeightMm' => (float) ( $view['printHeightMm'] ?? 0 ), 'areaX' => (float) ( $view['areaX'] ?? 0 ) / 100, 'areaY' => (float) ( $view['areaY'] ?? 0 ) / 100, 'areaWidth' => (float) ( $view['areaWidth'] ?? 0 ) / 100, 'areaHeight' => (float) ( $view['areaHeight'] ?? 0 ) / 100, 'surcharge' => (float) ( $view['surcharge'] ?? 0 ), ); if ( ! $remove ) { $indices[] = $i; if ( ! empty( $view['sheet'] ) ) { $sheets[ $i ] = true; } } } // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- checked above, numbers only. $custom_in = isset( $_POST['custom'] ) ? (array) wp_unslash( $_POST['custom'] ) : array(); $custom = array( 'enabled' => ! empty( $custom_in['enabled'] ) ); foreach ( array( 'minWidthMm', 'maxWidthMm', 'minHeightMm', 'maxHeightMm', 'pricePer100Cm2' ) as $key ) { if ( isset( $custom_in[ $key ] ) && '' !== $custom_in[ $key ] ) { $custom[ $key ] = (float) $custom_in[ $key ]; } } $saved = Eazign_Client::post( 'blanks/' . rawurlencode( $id ), array( 'name' => $text( 'name' ), 'productType' => $text( 'productType' ), 'technique' => $text( 'technique' ), 'basePrice' => (float) str_replace( ',', '.', $text( 'basePrice' ) ), 'sizes' => array_values( array_filter( array_map( 'trim', explode( ',', $text( 'sizes' ) ) ), 'strlen' ) ), 'skuPattern' => $text( 'skuPattern' ), // phpcs:ignore WordPress.Security.NonceVerification.Missing -- checked above. 'descriptionHtml' => isset( $_POST['descriptionHtml'] ) ? wp_kses_post( wp_unslash( $_POST['descriptionHtml'] ) ) : '', 'priceRules' => $current['blank']['priceRules'], 'customSize' => $custom, 'colors' => $colors, 'views' => $views, ) ); if ( is_wp_error( $saved ) ) { self::notice( 'error', $saved->get_error_message() ); wp_safe_redirect( $back ); exit; } // Base photos: the saved views come back in the order of the form. $notices = array(); foreach ( $indices as $position => $i ) { $view_id = $saved['blank']['views'][ $position ]['id'] ?? ''; $field = 'view_photo_' . $i; if ( ! $view_id ) { continue; } if ( ! empty( $sheets[ $i ] ) ) { $result = Eazign_Client::post( 'blanks/' . rawurlencode( $id ) . '/photo', array( 'viewId' => $view_id, 'sheet' => true, ) ); } elseif ( ! empty( $_FILES[ $field ]['tmp_name'] ) && is_uploaded_file( $_FILES[ $field ]['tmp_name'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- file path from PHP. $bytes = file_get_contents( $_FILES[ $field ]['tmp_name'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPress.Security.ValidatedSanitizedInput $result = Eazign_Client::post( 'blanks/' . rawurlencode( $id ) . '/photo', array( 'viewId' => $view_id, 'data' => base64_encode( $bytes ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- binary upload in JSON. ), array( 'timeout' => 120 ) ); } else { continue; } if ( is_wp_error( $result ) ) { $notices[] = $result->get_error_message(); } elseif ( ! empty( $result['notice'] ) ) { $notices[] = $result['notice']; } } self::notice( $notices ? 'warning' : 'success', $notices ? implode( ' · ', $notices ) : __( 'Blank saved.', 'eazign' ) ); wp_safe_redirect( $back ); exit; } /** * Delete blank. */ public static function delete_blank() { self::guard(); $id = isset( $_GET['blank'] ) ? sanitize_key( wp_unslash( $_GET['blank'] ) ) : ''; check_admin_referer( 'eazign_delete_blank_' . $id ); $result = Eazign_Client::post( 'blanks/' . rawurlencode( $id ) . '/delete' ); self::notice( is_wp_error( $result ) ? 'error' : 'success', is_wp_error( $result ) ? $result->get_error_message() : __( 'Blank deleted.', 'eazign' ) ); wp_safe_redirect( admin_url( 'admin.php?page=eazign-blanks' ) ); exit; } /** * Starts the designer product sync (background job at the service). */ public static function ajax_sync() { check_ajax_referer( 'eazign_sync' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to do this.', 'eazign' ) ), 403 ); } $id = isset( $_POST['blank'] ) ? sanitize_key( wp_unslash( $_POST['blank'] ) ) : ''; $status = isset( $_POST['status'] ) && 'draft' === $_POST['status'] ? 'draft' : 'publish'; $blank = Eazign_Client::get( 'blanks/' . rawurlencode( $id ) ); $result = Eazign_Client::post( 'blanks/' . rawurlencode( $id ) . '/sync', array( 'status' => $status ) ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } // Mockups of the current product: replaced ones are deleted when the job is done. $product = ! is_wp_error( $blank ) && ! empty( $blank['blank']['productId'] ) ? wc_get_product( (int) $blank['blank']['productId'] ) : null; if ( $product ) { set_transient( 'eazign_sync_images_' . sanitize_key( $result['jobId'] ), self::image_ids( $product ), HOUR_IN_SECONDS ); } wp_send_json_success( $result ); } /** * Image attachments of a product and its variations. * * @param WC_Product $product Product. * @return int[] */ private static function image_ids( $product ) { $ids = array_merge( array( (int) $product->get_image_id() ), array_map( 'intval', $product->get_gallery_image_ids() ) ); foreach ( $product->get_children() as $child_id ) { $child = wc_get_product( $child_id ); if ( $child ) { $ids[] = (int) $child->get_image_id(); } } return array_values( array_unique( array_filter( $ids ) ) ); } /** * Progress of the sync. */ public static function ajax_sync_status() { check_ajax_referer( 'eazign_sync' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to do this.', 'eazign' ) ), 403 ); } $job = isset( $_POST['job'] ) ? sanitize_key( wp_unslash( $_POST['job'] ) ) : ''; $result = Eazign_Client::get( 'sync/' . rawurlencode( $job ) ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } if ( 'done' === $result['status'] && ! empty( $result['productId'] ) ) { delete_transient( 'eazign_config_' . (int) $result['productId'] ); $product = wc_get_product( (int) $result['productId'] ); if ( $product ) { $result['permalink'] = $product->get_permalink(); $result['editUrl'] = get_edit_post_link( $product->get_id(), 'raw' ); // Remove the mockups WooCommerce downloaded for the previous version. $old = get_transient( 'eazign_sync_images_' . $job ); delete_transient( 'eazign_sync_images_' . $job ); if ( is_array( $old ) ) { foreach ( array_diff( $old, self::image_ids( $product ) ) as $attachment_id ) { if ( (int) wp_get_post_parent_id( $attachment_id ) === $product->get_id() ) { wp_delete_attachment( $attachment_id, true ); } } } } } wp_send_json_success( $result ); } // ---- print files ------------------------------------------------------------- /** * Recent print jobs. */ public static function page_files() { echo '

' . esc_html__( 'Print files', 'eazign' ) . '

'; if ( ! Eazign_Client::is_connected() ) { echo '

' . wp_kses_post( sprintf( /* translators: %s: URL */ __( 'Please connect your shop first.', 'eazign' ), esc_url( admin_url( 'admin.php?page=eazign' ) ) ) ) . '

'; return; } $data = Eazign_Client::get( 'jobs' ); if ( is_wp_error( $data ) ) { self::error_box( $data ); echo ''; return; } echo ''; echo ''; echo ''; if ( empty( $data['jobs'] ) ) { echo ''; } foreach ( $data['jobs'] as $job ) { Eazign_Orders::job_row( $job, true ); } echo '
' . esc_html__( 'Preview', 'eazign' ) . '' . esc_html__( 'Order', 'eazign' ) . '' . esc_html__( 'Variant', 'eazign' ) . '' . esc_html__( 'Status', 'eazign' ) . '' . esc_html__( 'Files', 'eazign' ) . '
' . esc_html__( 'No print files yet. They are created when a designed order is paid.', 'eazign' ) . '
'; } }