PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
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
← All changes | includes/Templates/Frontend.php +51 -20 1.9.141.10.18 View file →
@@ -9,11 +9,15 @@
9 9 */
10 10
11 11 namespace WCPOS\WooCommercePOS\Templates;
12 12
13 -use Ramsey\Uuid\Uuid;
14 13 use WCPOS\WooCommercePOS\Services\Auth;
14 +use WCPOS\WooCommercePOS\Services\Cashier;
15 +use WCPOS\WooCommercePOS\Services\Lifecycle_Events;
16 +use WCPOS\WooCommercePOS\Services\Settings;
17 +use WCPOS\WooCommercePOS\Sync\Pos_Uuid;
15 18 use WCPOS\WooCommercePOS\Template_Router;
19 +use const WCPOS\WooCommercePOS\PLUGIN_PATH;
16 20 use const WCPOS\WooCommercePOS\PLUGIN_URL;
17 21 use const WCPOS\WooCommercePOS\SHORT_NAME;
18 22 use const WCPOS\WooCommercePOS\VERSION;
19 23
@@ -38,9 +42,9 @@
38 42 * @return void
39 43 */
40 44 public function get_template(): void {
41 45 // force ssl.
42 - if ( ! is_ssl() && woocommerce_pos_get_settings( 'general', 'force_ssl' ) ) {
46 + if ( ! is_ssl() && Settings::instance()->force_ssl_enabled() ) {
43 47 wp_safe_redirect( woocommerce_pos_url() );
44 48 exit;
45 49 }
46 50
@@ -50,11 +54,11 @@
50 54 auth_redirect();
51 55 }
52 56
53 57 // check privileges.
54 - if ( ! current_user_can( 'access_woocommerce_pos' ) ) {
55 - // translators: Authorization error shown when a logged-in user lacks permission to open the POS page.
56 - wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'woocommerce-pos' ) );
58 + $user = wp_get_current_user();
59 + if ( ! Cashier::instance()->can_open_pos( $user ) ) {
60 + wp_die( esc_html( Cashier::instance()->missing_pos_capabilities_message( $user ) ), '', array( 'response' => 403 ) );
57 61 }
58 62
59 63 // disable cache plugins.
60 64 $this->no_cache();
@@ -80,8 +84,17 @@
80 84 $user = wp_get_current_user();
81 85 $auth_service = Auth::instance();
82 86 $this->wp_credentials = $auth_service->get_user_data( $user, true );
83 87
88 + // The activation funnel's step the admin side cannot see: the POS itself
89 + // being opened. Recorded here rather than by tracking the menu link, so
90 + // a bookmark, a direct URL or a till that never touches wp-admin all
91 + // count — and so it counts opens, not clicks that may never arrive.
92 + //
93 + // Everything above has already established that this is a logged-in user
94 + // with `access_woocommerce_pos`, past the SSL redirect.
95 + ( new Lifecycle_Events() )->report_app_opened();
96 +
84 97 include woocommerce_pos_locate_template( 'pos.php' );
85 98 exit;
86 99 }
87 100
@@ -142,8 +155,18 @@
142 155
143 156 // Default to the plugin's own major.minor so the stable lane tracks the
144 157 // version automatically: a 1.9.x plugin loads `@1.9`, a 1.10.x plugin loads
145 158 // `@1.10`, etc. — no edit needed as versions roll.
159 +
160 + /*
161 + * One jsDelivr ref per lane, named after the lane (owner ruling, 2026-09-04):
162 + * released lane → `@<major.minor>` (this default; the tag is cut at release)
163 + * next lane → `@next` — the `next` BRANCH of wcpos/web-bundle IS the dev
164 + * lane's tag. There is no versioned/prerelease tag for `next`.
165 + * dev-next sets WCPOS_WEB_BUNDLE_REF=next to load it.
166 + */
167 + // When `next` becomes `main`, the released ref simply becomes the new
168 + // major.minor (e.g. `@1.11`) via this default.
146 169 $default_bundle_ref = implode( '.', \array_slice( explode( '.', VERSION ), 0, 2 ) );
147 170
148 171 /**
149 172 * The web-bundle ref served from jsDelivr (or a full base URL).
@@ -149,10 +172,12 @@
149 172 * The web-bundle ref served from jsDelivr (or a full base URL).
150 173 *
151 174 * Override via the WCPOS_WEB_BUNDLE_REF constant / env var or this filter to
152 175 * point a site at another lane for testing the in-development build locally
153 - * or on staging: a branch (e.g. `next`), a tag, a commit, or a full base URL
154 - * (anything containing `://`, e.g. a local dev server or an EAS preview).
176 + * or on staging: the `next` lane's ref is the branch `next`
177 + * (https://cdn.jsdelivr.net/gh/wcpos/web-bundle@next); a tag, a commit, or a
178 + * full base URL (anything containing `://`, e.g. a local dev server or an EAS
179 + * preview) also work.
155 180 *
156 181 * @hook woocommerce_pos_web_bundle_ref
157 182 */
158 183 $bundle_ref = (string) apply_filters( 'woocommerce_pos_web_bundle_ref', $explicit_bundle_ref ?? $default_bundle_ref );
@@ -182,23 +207,21 @@
182 207 },
183 208 wcpos_get_stores()
184 209 );
185 210
186 - $site_uuid = get_option( 'woocommerce_pos_uuid' );
187 - if ( ! $site_uuid ) {
188 - $site_uuid = Uuid::uuid4()->toString();
189 - update_option( 'woocommerce_pos_uuid', $site_uuid );
211 + $site_uuid = wcpos_get_site_uuid();
212 + $opfs_worker_hash = hash_file( 'sha256', PLUGIN_PATH . 'assets/js/opfs.worker.js' );
213 + if ( false === $opfs_worker_hash ) {
214 + $opfs_worker_hash = VERSION;
190 215 }
191 216
192 - $user_uuid = get_user_meta( $user->ID, '_woocommerce_pos_uuid', true );
193 - if ( ! $user_uuid ) {
194 - $user_uuid = Uuid::uuid4()->toString();
195 - update_user_meta( $user->ID, '_woocommerce_pos_uuid', $user_uuid );
196 - }
217 + // Pos_Uuid is the sole authority for `_woocommerce_pos_uuid`: the value here
218 + // must match what /cashier and /customers serve, or the client forks identities.
219 + $user_uuid = Pos_Uuid::ensure_user_uuid( $user );
197 220
198 221 $vars = array(
199 222 'version' => VERSION,
200 - 'manifest' => $cdn_base_url . '/metadata.json?v=' . VERSION,
223 + 'manifest' => $cdn_base_url . '/metadata.json?v=' . $opfs_worker_hash,
201 224 'homepage' => woocommerce_pos_url(),
202 225 'logout_url' => $this->pos_logout_url(),
203 226 'site' => array(
204 227 'uuid' => $site_uuid,
@@ -212,9 +235,9 @@
212 235 'wc_version' => WC()->version,
213 236 'wcpos_version' => VERSION,
214 237 'wp_api_url' => get_rest_url(),
215 238 'wc_api_url' => trailingslashit( get_rest_url( null, 'wc/v3' ) ),
216 - 'wcpos_api_url' => trailingslashit( get_rest_url( null, 'wcpos/v1' ) ),
239 + 'wcpos_api_url' => trailingslashit( get_rest_url( null, 'wcpos/v2' ) ),
217 240 'wcpos_login_url' => Template_Router::get_auth_url(),
218 241 'locale' => get_locale(),
219 242 ),
220 243 'wp_credentials' => $this->wp_credentials,
@@ -239,9 +262,13 @@
239 262 /**
240 263 * Add path to worker scripts.
241 264 */
242 265 $idb_worker = PLUGIN_URL . 'assets/js/indexeddb.worker.js';
243 - $opfs_worker = PLUGIN_URL . 'assets/js/opfs.worker.js';
266 + $opfs_worker = add_query_arg(
267 + 'ver',
268 + $opfs_worker_hash,
269 + PLUGIN_URL . 'assets/js/opfs.worker.js'
270 + );
244 271
245 272 // getScript helper and initialProps.
246 273 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Inline JavaScript for POS frontend
247 274 echo "<script>
@@ -283,9 +310,13 @@
283 310 var baseUrl = '{$wcpos_base_path}';
284 311 </script>" . "\n";
285 312
286 313 echo "<script>
287 - var request = new Request(initialProps.manifest);
314 + // no-cache: revalidate the manifest with the CDN (ETag/304) on every boot.
315 + // jsDelivr serves it with max-age=604800 and the ?v= buster only changes on
316 + // plugin deploys, so a default fetch pins users to a stale bundle for up to
317 + // 7 days after a web-bundle publish.
318 + var request = new Request(initialProps.manifest, { cache: 'no-cache' });
288 319
289 320 window.fetch(request)
290 321 .then(function(response) { return response.json(); })
291 322 .then(function(data) {