PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.12
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.12
1.1.12 1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 All 36 releases
← All changes | includes/my-wordpress/integrations/woocommerce-relations.php +129 -20 1.0.0 → 1.1.12 View file →
@@ -59,8 +59,27 @@
59 59 */
60 60 const OPENSTATION_WOO_RELATION_ITEM_CAP = 20;
61 61
62 62 /**
63 + * How many orders the product and coupon groups list.
64 + */
65 +const OPENSTATION_WOO_RELATION_ORDER_CAP = 10;
66 +
67 +/**
68 + * How many order-item rows to read to fill that list.
69 + *
70 + * The id lists come out of `woocommerce_order_items`, which holds
71 + * refund rows alongside order rows — and refunds sort *first* there,
72 + * since the query orders by descending id and a refund is created
73 + * after the order it refunds. A `LIMIT 10` on a much-refunded product
74 + * could therefore come back as ten refunds and no orders at all, and
75 + * the group would render empty on the one product whose history a
76 + * merchant most wants to read. Reading a few times the budget and
77 + * stopping at the cap costs one bounded query.
78 + */
79 +const OPENSTATION_WOO_RELATION_ORDER_CANDIDATES = 40;
80 +
81 +/**
63 82 * Query flag marking a person-URL as a request for a *particular*
64 83 * view of that person rather than for the profile editor.
65 84 *
66 85 * Must stay equal to `OS_PERSON_VIEW_PARAM` in
@@ -130,8 +149,46 @@
130 149 return true === openstation_my_wordpress_woo_orders_permission();
131 150 }
132 151
133 152 /**
153 + * Whether an object read back from an order-item row is a purchase.
154 + *
155 + * Refunds keep their own line items in the same
156 + * `woocommerce_order_items` tables, under the refund's id — so a
157 + * lookup that asks those tables "which orders contain product X"
158 + * answers with refund ids too, for any product that has ever been
159 + * refunded. `WC_Order_Refund` extends `WC_Abstract_Order`, so the
160 + * usual guard waves it through, and the next line asks it for
161 + * `get_order_number()`: a `WC_Order` method the abstract base does
162 + * not declare, and therefore a fatal on the product edit screen.
163 + *
164 + * Dropping refunds is also the truer answer. "Who bought this" and
165 + * "where was this coupon used" are questions about purchases, and a
166 + * refund is the undoing of one.
167 + *
168 + * Deliberately *not* `instanceof WC_Order`. The abstract base is the
169 + * type every order class actually extends, including HPOS's overrides
170 + * and whatever custom order type a store registers — testing against
171 + * `WC_Order` has already been tried elsewhere in this integration and
172 + * silently emptied lists on stores that use them. So this excludes the
173 + * one known-hostile subclass and then asks the object directly for the
174 + * accessors these lists call, which keeps an exotic order type that
175 + * extends the base without them out of a fatal too.
176 + *
177 + * @param mixed $order Whatever `wc_get_order()` returned.
178 + * @return bool
179 + */
180 +function openstation_my_wordpress_woo_is_purchase( $order ) {
181 + if ( ! $order instanceof WC_Abstract_Order ) {
182 + return false;
183 + }
184 + if ( $order instanceof WC_Order_Refund ) {
185 + return false;
186 + }
187 + return method_exists( $order, 'get_order_number' );
188 +}
189 +
190 +/**
134 191 * The content identity for WooCommerce's product-reviews screen when
135 192 * it is filtered to a single product.
136 193 *
137 194 * `edit.php?post_type=product&page=product-reviews&product_id=N`.
@@ -278,8 +335,36 @@
278 335 return $links;
279 336 }
280 337
281 338 /**
339 + * The human label for an order: its number, plus the billing name
340 + * when the order has one.
341 + *
342 + * @param WC_Abstract_Order $order Order to label.
343 + * @return string
344 + */
345 +function openstation_my_wordpress_woo_order_title( $order ) {
346 + $name = method_exists( $order, 'get_formatted_billing_full_name' )
347 + ? trim( $order->get_formatted_billing_full_name() )
348 + : '';
349 +
350 + if ( '' !== $name ) {
351 + return sprintf(
352 + /* translators: 1: order number, 2: customer name. */
353 + __( 'Order #%1$s · %2$s', 'desktop-mode' ),
354 + $order->get_order_number(),
355 + $name
356 + );
357 + }
358 +
359 + return sprintf(
360 + /* translators: %s: order number. */
361 + __( 'Order #%s', 'desktop-mode' ),
362 + $order->get_order_number()
363 + );
364 +}
365 +
366 +/**
282 367 * Announce an identity for WooCommerce's own screens, and hang the
283 368 * shop's links off the identities the built-in detection already
284 369 * produces.
285 370 *
@@ -299,27 +384,12 @@
299 384 // branch), and it arrives with no links: an order's content is
300 385 // empty, so the hyperlink/media/term extractor finds nothing.
301 386 $order = openstation_my_wordpress_woo_current_order();
302 387 if ( $order && openstation_my_wordpress_woo_can_read_orders() ) {
303 - $name = method_exists( $order, 'get_formatted_billing_full_name' )
304 - ? trim( $order->get_formatted_billing_full_name() )
305 - : '';
306 -
307 388 $identity = array(
308 389 'type' => 'shop_order',
309 390 'id' => (int) $order->get_id(),
310 - 'label' => '' !== $name
311 - ? sprintf(
312 - /* translators: 1: order number, 2: customer name. */
313 - __( 'Order #%1$s · %2$s', 'desktop-mode' ),
314 - $order->get_order_number(),
315 - $name
316 - )
317 - : sprintf(
318 - /* translators: %s: order number. */
319 - __( 'Order #%s', 'desktop-mode' ),
320 - $order->get_order_number()
321 - ),
391 + 'label' => openstation_my_wordpress_woo_order_title( $order ),
322 392 );
323 393
324 394 $links = openstation_my_wordpress_woo_order_refs( $order );
325 395 if ( ! empty( $links ) ) {
@@ -729,13 +799,18 @@
729 799 // data reached from a product screen, and a shop editor who may
730 800 // not read orders must not read them sideways.
731 801 if ( openstation_my_wordpress_woo_can_read_orders() ) {
732 802 $customers = array();
733 - foreach ( openstation_my_wordpress_woo_orders_with_product( $product_id, 10 ) as $order_id ) {
803 + $listed = 0;
804 + foreach ( openstation_my_wordpress_woo_orders_with_product( $product_id, OPENSTATION_WOO_RELATION_ORDER_CANDIDATES ) as $order_id ) {
805 + if ( $listed >= OPENSTATION_WOO_RELATION_ORDER_CAP ) {
806 + break;
807 + }
734 808 $order = wc_get_order( $order_id );
735 - if ( ! $order instanceof WC_Abstract_Order ) {
809 + if ( ! openstation_my_wordpress_woo_is_purchase( $order ) ) {
736 810 continue;
737 811 }
812 + ++$listed;
738 813
739 814 $name = method_exists( $order, 'get_formatted_billing_full_name' )
740 815 ? trim( $order->get_formatted_billing_full_name() )
741 816 : '';
@@ -925,13 +1000,18 @@
925 1000 // nothing behind it, so "did this campaign work, and for whom" is
926 1001 // a question you currently answer by exporting orders.
927 1002 if ( openstation_my_wordpress_woo_can_read_orders() ) {
928 1003 $customers = array();
929 - foreach ( openstation_my_wordpress_woo_orders_with_coupon( $coupon->get_code(), 10 ) as $order_id ) {
1004 + $listed = 0;
1005 + foreach ( openstation_my_wordpress_woo_orders_with_coupon( $coupon->get_code(), OPENSTATION_WOO_RELATION_ORDER_CANDIDATES ) as $order_id ) {
1006 + if ( $listed >= OPENSTATION_WOO_RELATION_ORDER_CAP ) {
1007 + break;
1008 + }
930 1009 $order = wc_get_order( $order_id );
931 - if ( ! $order instanceof WC_Abstract_Order ) {
1010 + if ( ! openstation_my_wordpress_woo_is_purchase( $order ) ) {
932 1011 continue;
933 1012 }
1013 + ++$listed;
934 1014
935 1015 $name = method_exists( $order, 'get_formatted_billing_full_name' )
936 1016 ? trim( $order->get_formatted_billing_full_name() )
937 1017 : '';
@@ -1074,8 +1154,36 @@
1074 1154 return $related;
1075 1155 }
1076 1156
1077 1157 /**
1158 + * Name the order-edit window after the order it shows.
1159 + *
1160 + * @return void
1161 + */
1162 +function openstation_my_wordpress_woo_order_window_title() {
1163 + if ( ! openstation_my_wordpress_woo_active() || ! openstation_my_wordpress_woo_can_read_orders() ) {
1164 + return;
1165 + }
1166 +
1167 + $order = openstation_my_wordpress_woo_current_order();
1168 + if ( ! $order ) {
1169 + return;
1170 + }
1171 +
1172 + // `JSON_HEX_TAG` neutralises a `</script>` smuggled in through the billing name.
1173 + $title_json = wp_json_encode(
1174 + openstation_my_wordpress_woo_order_title( $order ),
1175 + JSON_HEX_TAG | JSON_UNESCAPED_SLASHES
1176 + );
1177 + if ( false === $title_json ) {
1178 + return;
1179 + }
1180 + wp_print_inline_script_tag(
1181 + 'window.parent.postMessage({type:"os-title-change",title:' . $title_json . '},window.location.origin);'
1182 + );
1183 +}
1184 +
1185 +/**
1078 1186 * Boot the relations wiring.
1079 1187 *
1080 1188 * Priority 20 on the identity filter so a site that overrides the
1081 1189 * order identity for its own reasons still wins.
@@ -1084,6 +1192,7 @@
1084 1192 */
1085 1193 function openstation_my_wordpress_woo_relations_boot() {
1086 1194 add_filter( 'openstation_window_content_identity', 'openstation_my_wordpress_woo_content_identity', 20, 2 );
1087 1195 add_filter( 'openstation_window_related_entities', 'openstation_my_wordpress_woo_related_entities', 20, 3 );
1196 + add_action( 'openstation_chromeless_after', 'openstation_my_wordpress_woo_order_window_title' );
1088 1197 }
1089 1198 openstation_my_wordpress_woo_relations_boot();