PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.9.15
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.9.15
1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
woocommerce-pos / includes / Services / Cloud_Print_Registry.php

Cloud_Print_Registry.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.9.15, at includes/Services/Cloud_Print_Registry.php

261 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Cloud printer registry (reads woocommerce_pos_settings_cloud_print).
4 *
5 * @package WCPOS\WooCommercePOS\Services
6 */
7
8 namespace WCPOS\WooCommercePOS\Services;
9
10 /**
11 * Cloud_Print_Registry class.
12 */
13 class Cloud_Print_Registry {
14 const OPTION = 'woocommerce_pos_settings_cloud_print';
15
16 const RUNTIME_OPTION = 'woocommerce_pos_cloud_print_runtime';
17 const SEEN_TTL = 150; // Seconds; connected if seen within this window.
18 const PN_STATUS_TTL = 60; // Seconds; PrintNode live-status cache window.
19
20 /**
21 * All registered cloud printers.
22 *
23 * @return array<int, array>
24 */
25 public function get_printers(): array {
26 $settings = get_option( self::OPTION, array() );
27
28 return isset( $settings['printers'] ) && \is_array( $settings['printers'] ) ? $settings['printers'] : array();
29 }
30
31 /**
32 * Get a registered cloud printer by id.
33 *
34 * @param string $printer_id Printer id.
35 *
36 * @return array|null
37 */
38 public function get_printer( string $printer_id ): ?array {
39 foreach ( $this->get_printers() as $printer ) {
40 if ( isset( $printer['id'] ) && hash_equals( (string) $printer['id'], $printer_id ) ) {
41 return $printer;
42 }
43 }
44
45 return null;
46 }
47
48 /**
49 * Verify a printer's poll token (constant-time).
50 *
51 * @param string $printer_id Printer id.
52 * @param string $token Presented token.
53 */
54 public function verify_token( string $printer_id, string $token ): bool {
55 $printer = $this->get_printer( $printer_id );
56 if ( null === $printer || empty( $printer['poll_token_hash'] ) || '' === $token ) {
57 return false;
58 }
59
60 return hash_equals( (string) $printer['poll_token_hash'], self::hash_token( $token ) );
61 }
62
63 /**
64 * Generate a cryptographically strong poll token (returned to the admin once).
65 */
66 public static function generate_token(): string {
67 return bin2hex( random_bytes( 24 ) );
68 }
69
70 /**
71 * Hash a poll token for at-rest storage (we never persist the plaintext).
72 *
73 * @param string $token Token.
74 */
75 public static function hash_token( string $token ): string {
76 return hash( 'sha256', $token );
77 }
78
79 /**
80 * Derive a stable, URL-safe printer id from a display name, unique against existing ids.
81 *
82 * @param string $name Display name.
83 * @param array<string> $existing_ids Already-used ids.
84 *
85 * @return string
86 */
87 public static function derive_id( string $name, array $existing_ids ): string {
88 $base = sanitize_title( $name );
89 if ( '' === $base ) {
90 $base = 'printer';
91 }
92 $candidate = $base;
93 $suffix = 2;
94 while ( \in_array( $candidate, $existing_ids, true ) ) {
95 $candidate = $base . '-' . $suffix;
96 ++$suffix;
97 }
98
99 return $candidate;
100 }
101
102 /**
103 * Record that a printer polled just now.
104 *
105 * @param string $printer_id Printer id.
106 */
107 public function record_seen( string $printer_id ): void {
108 $runtime = get_option( self::RUNTIME_OPTION, array() );
109 $runtime = \is_array( $runtime ) ? $runtime : array();
110 $runtime[ $printer_id ] = time();
111 update_option( self::RUNTIME_OPTION, $runtime, false ); // Autoload no.
112 }
113
114 /**
115 * Get a printer's last-seen unix timestamp (0 if never).
116 *
117 * @param string $printer_id Printer id.
118 *
119 * @return int
120 */
121 public function get_seen( string $printer_id ): int {
122 $runtime = get_option( self::RUNTIME_OPTION, array() );
123
124 return ( \is_array( $runtime ) && isset( $runtime[ $printer_id ] ) ) ? (int) $runtime[ $printer_id ] : 0;
125 }
126
127 /**
128 * Drop runtime last-seen entries for printer ids that no longer exist.
129 *
130 * Prevents the runtime option from growing unbounded as printers are
131 * removed, and stops a recreated id (slug reuse) from inheriting a deleted
132 * printer's stale status.
133 *
134 * @param array<string> $keep_ids Printer ids to retain.
135 */
136 public function prune_seen( array $keep_ids ): void {
137 $runtime = get_option( self::RUNTIME_OPTION, array() );
138 if ( ! \is_array( $runtime ) ) {
139 return;
140 }
141 $pruned = array_intersect_key( $runtime, array_flip( $keep_ids ) );
142 if ( $pruned !== $runtime ) {
143 update_option( self::RUNTIME_OPTION, $pruned, false );
144 }
145 }
146
147 /**
148 * Connection status for a printer.
149 *
150 * For PrintNode printers this returns PrintNode's live vocabulary
151 * ('online'|'offline'|'unknown'), cached briefly. For polling printers
152 * (Star/Epson) it returns 'waiting' (never polled), 'connected' (polled
153 * within SEEN_TTL), or 'offline' (polled, but stale).
154 *
155 * @param string $printer_id Printer id.
156 *
157 * @return string
158 */
159 public function status_for( string $printer_id ): string {
160 $printer = $this->get_printer( $printer_id );
161 if ( null !== $printer && 'printnode' === ( $printer['provider'] ?? '' ) ) {
162 return $this->printnode_status( $printer );
163 }
164
165 if ( null !== $printer && 'star-online' === ( $printer['provider'] ?? '' ) ) {
166 return $this->star_online_status( $printer );
167 }
168
169 if ( null !== $printer && Provider::is_polling( (string) ( $printer['provider'] ?? '' ) ) ) {
170 $relay_status = Cloud_Print_Relay_Service::status( $printer_id );
171 if ( null !== $relay_status && 'blocked' === $relay_status['origin_status'] ) {
172 return 'blocked';
173 }
174 if ( null !== $relay_status && null !== $relay_status['last_seen_seconds_ago'] && $relay_status['last_seen_seconds_ago'] <= self::SEEN_TTL ) {
175 return 'connected';
176 }
177 }
178
179 $seen = $this->get_seen( $printer_id );
180 if ( 0 === $seen ) {
181 return 'waiting';
182 }
183
184 return ( time() - $seen ) <= self::SEEN_TTL ? 'connected' : 'offline';
185 }
186
187 /**
188 * The relay's block signal for a printer, when it reports one.
189 *
190 * Delegates to the relay service's transient-cached status, so this is
191 * safe to call in any order relative to status_for().
192 *
193 * @param string $printer_id Printer ID.
194 *
195 * @return string|null
196 */
197 public function status_detail_for( string $printer_id ): ?string {
198 return Cloud_Print_Relay_Service::status_detail( $printer_id );
199 }
200
201 /**
202 * Resolve a PrintNode printer's live status, cached for PN_STATUS_TTL seconds.
203 *
204 * All outcomes are cached, including 'unknown'/'offline', to avoid hammering
205 * the PrintNode API on every settings read.
206 *
207 * @param array $printer Registered PrintNode printer.
208 *
209 * @return string 'online', 'offline', or 'unknown'.
210 */
211 private function printnode_status( array $printer ): string {
212 $key = 'wcpos_cloud_print_pn_status_' . md5( (string) $printer['id'] );
213 $cached = get_transient( $key );
214 if ( false !== $cached ) {
215 return (string) $cached;
216 }
217
218 $api_key = (string) ( $printer['printnode_api_key'] ?? '' );
219 $pn_printer_id = (int) ( $printer['printnode_printer_id'] ?? 0 );
220 if ( '' === $api_key || 0 === $pn_printer_id ) {
221 $status = 'unknown';
222 } else {
223 $status = ( new PrintNode_Client( $api_key ) )->printer_state( $pn_printer_id );
224 }
225
226 set_transient( $key, $status, self::PN_STATUS_TTL );
227
228 return $status;
229 }
230 /**
231 * Resolve a Star Online device's live status, cached for PN_STATUS_TTL seconds.
232 *
233 * @param array $printer Registered star-online printer.
234 *
235 * @return string 'online', 'offline', or 'unknown'.
236 */
237 private function star_online_status( array $printer ): string {
238 $key = 'wcpos_cloud_print_star_status_' . md5( (string) $printer['id'] );
239 $cached = get_transient( $key );
240 if ( false !== $cached ) {
241 return (string) $cached;
242 }
243
244 $api_key = (string) ( $printer['star_api_key'] ?? '' );
245 $url = (string) ( $printer['star_cloudprnt_url'] ?? '' );
246 $device_id = (string) ( $printer['star_device_id'] ?? '' );
247 $api_base = Star_Online_Client::api_base_from_cloudprnt_url( $url );
248 $group = Star_Online_Client::group_from_cloudprnt_url( $url );
249
250 if ( '' === $api_key || null === $api_base || '' === $group || '' === $device_id ) {
251 $status = 'unknown';
252 } else {
253 $status = ( new Star_Online_Client( $api_base, $api_key ) )->device_state( $group, $device_id );
254 }
255
256 set_transient( $key, $status, self::PN_STATUS_TTL );
257
258 return $status;
259 }
260 }
261