Templates
4 weeks ago
AdminManager.php
10 months ago
ListTable.php
10 months ago
MenusController.php
10 months ago
NotificationCreatePage.php
10 months ago
NotificationEditPage.php
4 weeks ago
NotificationsPage.php
4 weeks ago
SettingsController.php
10 months ago
ListTable.php
825 lines
| 1 | <?php |
| 2 | |
| 3 | declare( strict_types = 1 ); |
| 4 | |
| 5 | namespace Automattic\WooCommerce\Internal\StockNotifications\Admin; |
| 6 | |
| 7 | use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore; |
| 8 | use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus; |
| 9 | use Automattic\WooCommerce\Internal\StockNotifications\Notification; |
| 10 | use Automattic\WooCommerce\Internal\StockNotifications\Factory; |
| 11 | use Automattic\WooCommerce\Internal\StockNotifications\Admin\NotificationsPage; |
| 12 | use Automattic\WooCommerce\Internal\StockNotifications\Utilities\EligibilityService; |
| 13 | |
| 14 | /** |
| 15 | * Notifications list table for Customer Stock Notifications. |
| 16 | */ |
| 17 | class ListTable extends \WP_List_Table { |
| 18 | |
| 19 | /** |
| 20 | * Total view records. |
| 21 | * |
| 22 | * @var int |
| 23 | */ |
| 24 | public $total_items = 0; |
| 25 | |
| 26 | /** |
| 27 | * Total active records. |
| 28 | * |
| 29 | * @var int |
| 30 | */ |
| 31 | public $total_active_items = 0; |
| 32 | |
| 33 | /** |
| 34 | * Total pending records. |
| 35 | * |
| 36 | * @var int |
| 37 | */ |
| 38 | public $total_pending_items = 0; |
| 39 | |
| 40 | /** |
| 41 | * Total cancelled records. |
| 42 | * |
| 43 | * @var int |
| 44 | */ |
| 45 | public $total_cancelled_items = 0; |
| 46 | |
| 47 | /** |
| 48 | * Total sent records. |
| 49 | * |
| 50 | * @var int |
| 51 | */ |
| 52 | public $total_sent_items = 0; |
| 53 | |
| 54 | /** |
| 55 | * Has stock notifications. |
| 56 | * |
| 57 | * @var bool |
| 58 | */ |
| 59 | public $has_stock_notifications = false; |
| 60 | |
| 61 | /** |
| 62 | * Data store. |
| 63 | * |
| 64 | * @var StockNotificationsDataStore |
| 65 | */ |
| 66 | public $data_store; |
| 67 | |
| 68 | /** |
| 69 | * Eligibility service. |
| 70 | * |
| 71 | * @var EligibilityService |
| 72 | */ |
| 73 | public $eligibility_service; |
| 74 | |
| 75 | /** |
| 76 | * Init. |
| 77 | * |
| 78 | * @internal |
| 79 | * |
| 80 | * @param EligibilityService $eligibility_service Eligibility service. |
| 81 | */ |
| 82 | final public function init( EligibilityService $eligibility_service ) { |
| 83 | $this->eligibility_service = $eligibility_service; |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Constructor. |
| 88 | * |
| 89 | * @return void |
| 90 | */ |
| 91 | public function __construct() { |
| 92 | |
| 93 | $this->data_store = \WC_Data_Store::load( 'stock_notification' ); |
| 94 | $this->has_stock_notifications = $this->data_store->query( array( 'return' => 'count' ) ) > 0; |
| 95 | |
| 96 | parent::__construct( |
| 97 | array( |
| 98 | 'singular' => 'woocommerce_stock_notification', |
| 99 | 'plural' => 'woocommerce_stock_notifications', |
| 100 | ) |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Handles the checkbox column output. |
| 106 | * |
| 107 | * @param Notification $notification The notification object. |
| 108 | * @return void |
| 109 | */ |
| 110 | public function column_cb( $notification ) { |
| 111 | ?><label class="screen-reader-text" for="cb-select-<?php echo absint( $notification->get_id() ); ?>"> |
| 112 | <?php |
| 113 | /* translators: %s: Notification code */ |
| 114 | printf( esc_html__( 'Select %s', 'woocommerce' ), esc_html( $notification->get_id() ) ); |
| 115 | ?> |
| 116 | </label> |
| 117 | <input id="cb-select-<?php echo absint( $notification->get_id() ); ?>" type="checkbox" name="notification[]" value="<?php echo absint( $notification->get_id() ); ?>" /> |
| 118 | <?php |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Handles the title column output. |
| 123 | * |
| 124 | * @param Notification $notification The notification object. |
| 125 | * @return void |
| 126 | */ |
| 127 | public function column_id( $notification ) { |
| 128 | $actions = array( |
| 129 | 'edit' => sprintf( '<a href="' . admin_url( NotificationsPage::PAGE_URL . '¬ification_action=edit¬ification_id=%d' ) . '">%s</a>', $notification->get_id(), __( 'Edit', 'woocommerce' ) ), |
| 130 | 'delete' => sprintf( '<a href="' . wp_nonce_url( admin_url( NotificationsPage::PAGE_URL . '¬ification_action=delete¬ification_id=%d' ), 'delete_customer_stock_notification' ) . '">%s</a>', $notification->get_id(), __( 'Delete', 'woocommerce' ) ), |
| 131 | ); |
| 132 | |
| 133 | $title = $notification->get_id(); |
| 134 | |
| 135 | printf( |
| 136 | '<a class="row-title" href="%s" aria-label="%s">#%s</a>%s', |
| 137 | esc_url( admin_url( NotificationsPage::PAGE_URL . '¬ification_action=edit¬ification_id=' . $notification->get_id() ) ), |
| 138 | /* translators: %s: Notification code */ |
| 139 | sprintf( esc_attr__( '“%s” (Edit)', 'woocommerce' ), esc_attr( $title ) ), |
| 140 | esc_html( $title ), |
| 141 | wp_kses_post( $this->row_actions( $actions ) ) |
| 142 | ); |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Handles the status column output. |
| 147 | * |
| 148 | * @param Notification $notification The notification object. |
| 149 | * @return void |
| 150 | */ |
| 151 | public function column_status( $notification ) { |
| 152 | |
| 153 | if ( $notification->get_status() === NotificationStatus::PENDING ) { |
| 154 | $status = 'cancelled'; |
| 155 | $label = _x( 'Pending', 'stock notification status', 'woocommerce' ); |
| 156 | } elseif ( $notification->get_status() === NotificationStatus::CANCELLED ) { |
| 157 | $status = 'cancelled'; |
| 158 | $label = _x( 'Cancelled', 'stock notification status', 'woocommerce' ); |
| 159 | } elseif ( $notification->get_status() === NotificationStatus::SENT ) { |
| 160 | $status = 'cancelled'; |
| 161 | $label = _x( 'Sent', 'stock notification status', 'woocommerce' ); |
| 162 | } else { |
| 163 | $status = 'completed'; |
| 164 | $label = _x( 'Active', 'stock notification status', 'woocommerce' ); |
| 165 | } |
| 166 | |
| 167 | printf( '<mark class="order-status %s"><span>%s</span></mark>', esc_attr( sanitize_html_class( 'status-' . $status ) ), esc_html( $label ) ); |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Handles the redeemed user column output. |
| 172 | * |
| 173 | * @param Notification $notification The notification object. |
| 174 | * @return void |
| 175 | */ |
| 176 | public function column_user( $notification ) { |
| 177 | if ( $notification->get_user_id() ) { |
| 178 | $user = get_user_by( 'id', $notification->get_user_id() ); |
| 179 | } |
| 180 | |
| 181 | if ( isset( $user ) && $user ) { |
| 182 | printf( '<a href="%s" target="_blank">%s</a>', esc_url( get_edit_user_link( $user->ID ) ), esc_html( $user->display_name ) ); |
| 183 | } else { |
| 184 | echo esc_html( $notification->get_user_email() ); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Handles the product column output. |
| 190 | * |
| 191 | * @param Notification $notification The notification object. |
| 192 | * @return void |
| 193 | */ |
| 194 | public function column_product( $notification ) { |
| 195 | $product = $notification->get_product(); |
| 196 | |
| 197 | if ( ! is_a( $product, 'WC_Product' ) ) { |
| 198 | echo '—'; |
| 199 | return; |
| 200 | } |
| 201 | |
| 202 | $name = $product->get_name(); |
| 203 | $formatted_variation_list = $this->get_product_formatted_variation_list( true ); |
| 204 | |
| 205 | if ( $formatted_variation_list ) { |
| 206 | /* translators: product name, identifier */ |
| 207 | $name .= '<span class="description">' . $formatted_variation_list . '</span>'; |
| 208 | } |
| 209 | |
| 210 | echo wp_kses_post( |
| 211 | sprintf( |
| 212 | '<a target="_blank" href="' . admin_url( 'post.php?post=%d&action=edit' ) . '">%s</a>', |
| 213 | $product->get_parent_id() ? absint( $product->get_parent_id() ) : absint( $product->get_id() ), |
| 214 | $name |
| 215 | ) |
| 216 | ); |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Handles the product SKU output. |
| 221 | * |
| 222 | * @param Notification $notification The notification object. |
| 223 | * @return void |
| 224 | */ |
| 225 | public function column_sku( $notification ) { |
| 226 | $product = $notification->get_product(); |
| 227 | $sku = false; |
| 228 | |
| 229 | if ( is_a( $product, 'WC_Product' ) ) { |
| 230 | $sku = $product->get_sku(); |
| 231 | } |
| 232 | |
| 233 | if ( $sku ) { |
| 234 | echo wp_kses_post( $sku ); |
| 235 | } else { |
| 236 | echo '—'; |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | /** |
| 241 | * Handles the notification date column output. |
| 242 | * |
| 243 | * @param Notification $notification The notification object. |
| 244 | * @return void |
| 245 | */ |
| 246 | public function column_date_created_gmt( $notification ) { |
| 247 | $date_created = $notification->get_date_created(); |
| 248 | |
| 249 | if ( ! $date_created ) { |
| 250 | $t_time = __( '—', 'woocommerce' ); |
| 251 | $h_time = $t_time; |
| 252 | } else { |
| 253 | $date_created = $date_created->getTimestamp(); |
| 254 | $t_time = date_i18n( _x( 'Y/m/d g:i:s a', 'list table date hover format', 'woocommerce' ), $date_created ); |
| 255 | $h_time = date_i18n( wc_date_format(), $date_created ); |
| 256 | } |
| 257 | |
| 258 | echo '<span title="' . esc_attr( $t_time ) . '">' . esc_html( $h_time ) . '</span>'; |
| 259 | } |
| 260 | |
| 261 | /** |
| 262 | * Message to be displayed when there are no items. |
| 263 | * |
| 264 | * @return void |
| 265 | */ |
| 266 | public function no_items() { |
| 267 | ?> |
| 268 | <p class="main"> |
| 269 | <?php esc_html_e( 'No Notifications found', 'woocommerce' ); ?> |
| 270 | </p> |
| 271 | <?php |
| 272 | } |
| 273 | |
| 274 | /** |
| 275 | * Get a list of columns. The format is: |
| 276 | * 'internal-name' => 'Title' |
| 277 | */ |
| 278 | public function get_columns() { |
| 279 | |
| 280 | $columns = array(); |
| 281 | $columns['cb'] = '<input type="checkbox" />'; |
| 282 | $columns['id'] = _x( 'Notification', 'column_name', 'woocommerce' ); |
| 283 | $columns['status'] = _x( 'Status', 'column_name', 'woocommerce' ); |
| 284 | $columns['user'] = _x( 'User/Email', 'column_name', 'woocommerce' ); |
| 285 | $columns['product'] = _x( 'Product', 'column_name', 'woocommerce' ); |
| 286 | $columns['sku'] = _x( 'SKU', 'column_name', 'woocommerce' ); |
| 287 | $columns['date_created_gmt'] = _x( 'Signed Up', 'column_name', 'woocommerce' ); |
| 288 | |
| 289 | return $columns; |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Return sortable columns. |
| 294 | * |
| 295 | * @return array |
| 296 | */ |
| 297 | public function get_sortable_columns() { |
| 298 | $sortable_columns = array( |
| 299 | 'id' => array( 'id', true ), |
| 300 | 'product' => array( 'product_id', true ), |
| 301 | ); |
| 302 | |
| 303 | return $sortable_columns; |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * Returns bulk actions. |
| 308 | * |
| 309 | * @return array |
| 310 | */ |
| 311 | protected function get_bulk_actions() { |
| 312 | $actions = array(); |
| 313 | $actions['enable'] = __( 'Activate', 'woocommerce' ); |
| 314 | $actions['cancel'] = __( 'Cancel', 'woocommerce' ); |
| 315 | $actions['delete'] = __( 'Delete permanently', 'woocommerce' ); |
| 316 | return $actions; |
| 317 | } |
| 318 | |
| 319 | /** |
| 320 | * Query the DB and attach items. |
| 321 | * |
| 322 | * @return void |
| 323 | */ |
| 324 | public function prepare_items() { |
| 325 | $per_page = (int) get_user_meta( get_current_user_id(), 'stock_notifications_per_page', true ); |
| 326 | $per_page = $per_page > 0 ? $per_page : 10; |
| 327 | |
| 328 | // Table columns. |
| 329 | $columns = $this->get_columns(); |
| 330 | $hidden = array(); |
| 331 | $sortable = $this->get_sortable_columns(); |
| 332 | $this->_column_headers = array( $columns, $hidden, $sortable ); |
| 333 | |
| 334 | // Setup params. |
| 335 | $paged = isset( $_REQUEST['paged'] ) ? max( 0, (int) wp_unslash( $_REQUEST['paged'] ) - 1 ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 336 | $orderby = ( isset( $_REQUEST['orderby'] ) && in_array( wp_unslash( $_REQUEST['orderby'] ), array_keys( $this->get_sortable_columns() ), true ) ) ? wc_clean( wp_unslash( $_REQUEST['orderby'] ) ) : 'id'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 337 | $order = ( isset( $_REQUEST['order'] ) && in_array( wp_unslash( $_REQUEST['order'] ), array( 'asc', 'desc' ), true ) ) ? wc_clean( wp_unslash( $_REQUEST['order'] ) ) : 'desc'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 338 | |
| 339 | // Query args. |
| 340 | $query_args = array( |
| 341 | 'order_by' => array( $orderby => $order ), |
| 342 | 'limit' => $per_page, |
| 343 | 'offset' => $paged * $per_page, |
| 344 | ); |
| 345 | |
| 346 | // Search. |
| 347 | if ( isset( $_REQUEST['s'] ) && ! empty( $_REQUEST['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 348 | $query_args['user_email'] = wc_clean( wp_unslash( $_REQUEST['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 349 | } |
| 350 | |
| 351 | // Views. |
| 352 | if ( ! empty( $_REQUEST['status'] ) && 'active_customer_stock_notifications' === $_REQUEST['status'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 353 | $query_args['status'] = NotificationStatus::ACTIVE; |
| 354 | } elseif ( ! empty( $_REQUEST['status'] ) && 'sent_customer_stock_notifications' === $_REQUEST['status'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 355 | $query_args['status'] = NotificationStatus::SENT; |
| 356 | } elseif ( ! empty( $_REQUEST['status'] ) && 'cancelled_customer_stock_notifications' === $_REQUEST['status'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 357 | $query_args['status'] = NotificationStatus::CANCELLED; |
| 358 | } elseif ( ! empty( $_REQUEST['status'] ) && 'pending_customer_stock_notifications' === $_REQUEST['status'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 359 | $query_args['status'] = NotificationStatus::PENDING; |
| 360 | } |
| 361 | |
| 362 | // Filters. |
| 363 | if ( ! empty( $_GET['m'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 364 | $filter = absint( wp_unslash( $_GET['m'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 365 | $month = substr( (string) $filter, 4, 6 ); |
| 366 | $year = substr( (string) $filter, 0, 4 ); // This will break at year 10.000 AC :). |
| 367 | |
| 368 | $start_timestamp = mktime( 0, 0, 0, (int) $month, 1, (int) $year ); |
| 369 | $query_args['start_date'] = gmdate( 'Y-m-d H:i:s', $start_timestamp ); |
| 370 | |
| 371 | $end_timestamp = mktime( 0, 0, 0, (int) $month + 1, 1, (int) $year ); |
| 372 | $query_args['end_date'] = gmdate( 'Y-m-d H:i:s', $end_timestamp ); |
| 373 | } |
| 374 | |
| 375 | if ( ! empty( $_GET['customer_stock_notifications_product_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 376 | $filter = absint( wp_unslash( $_GET['customer_stock_notifications_product_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 377 | $product = wc_get_product( $filter ); |
| 378 | if ( $product instanceof \WC_Product ) { |
| 379 | $target_ids = $this->eligibility_service->get_target_product_ids( $product ); |
| 380 | $query_args['product_id'] = $target_ids; |
| 381 | } else { |
| 382 | NotificationsPage::add_notice( __( 'Invalid product selected.', 'woocommerce' ), 'error' ); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | if ( ! empty( $_GET['customer_stock_notifications_customer_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 387 | $filter = absint( wp_unslash( $_GET['customer_stock_notifications_customer_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 388 | $query_args['user_id'] = array( $filter ); |
| 389 | } |
| 390 | |
| 391 | $query_args['return'] = 'objects'; |
| 392 | $this->items = $this->data_store->query( $query_args ); |
| 393 | |
| 394 | // Count total items. |
| 395 | $query_args['return'] = 'count'; |
| 396 | unset( $query_args['limit'] ); |
| 397 | unset( $query_args['offset'] ); |
| 398 | $this->total_items = $this->data_store->query( $query_args ); |
| 399 | |
| 400 | // Count active. |
| 401 | $query_args['status'] = NotificationStatus::ACTIVE; |
| 402 | $this->total_active_items = $this->data_store->query( $query_args ); |
| 403 | |
| 404 | // Count sent. |
| 405 | $query_args['status'] = NotificationStatus::SENT; |
| 406 | $this->total_sent_items = $this->data_store->query( $query_args ); |
| 407 | |
| 408 | // Count cancelled. |
| 409 | $query_args['status'] = NotificationStatus::CANCELLED; |
| 410 | $this->total_cancelled_items = $this->data_store->query( $query_args ); |
| 411 | |
| 412 | // Count pending. |
| 413 | $query_args['status'] = NotificationStatus::PENDING; |
| 414 | $this->total_pending_items = $this->data_store->query( $query_args ); |
| 415 | |
| 416 | // Configure pagination. |
| 417 | $this->set_pagination_args( |
| 418 | array( |
| 419 | 'total_items' => $this->total_items, // Total items defined above. |
| 420 | 'per_page' => $per_page, // Per page constant defined at top of method. |
| 421 | 'total_pages' => ceil( $this->total_items / $per_page ), // Calculate pages count. |
| 422 | ) |
| 423 | ); |
| 424 | } |
| 425 | |
| 426 | /** |
| 427 | * Display table extra nav. |
| 428 | * |
| 429 | * @param string $which top|bottom. |
| 430 | * @return void |
| 431 | */ |
| 432 | public function extra_tablenav( $which ) { |
| 433 | if ( 'top' === $which && ! is_singular() ) { |
| 434 | ?> |
| 435 | <div class="alignleft actions"> |
| 436 | <?php |
| 437 | $this->render_filters(); |
| 438 | submit_button( __( 'Filter', 'woocommerce' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) ); |
| 439 | ?> |
| 440 | </div> |
| 441 | <?php |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | /** |
| 446 | * Display table filters. |
| 447 | * |
| 448 | * @return void |
| 449 | */ |
| 450 | protected function render_filters() { |
| 451 | $this->display_months_dropdown(); |
| 452 | $this->display_customer_dropdown(); |
| 453 | $this->display_product_dropdown(); |
| 454 | } |
| 455 | |
| 456 | /** |
| 457 | * Display product filter. |
| 458 | * |
| 459 | * @return void |
| 460 | */ |
| 461 | protected function display_product_dropdown() { |
| 462 | $product_string = ''; |
| 463 | $product_id = ''; |
| 464 | |
| 465 | if ( ! empty( $_GET['customer_stock_notifications_product_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 466 | $product_id = wc_clean( wp_unslash( $_GET['customer_stock_notifications_product_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 467 | $product = wc_get_product( absint( $product_id ) ); |
| 468 | |
| 469 | if ( $product ) { |
| 470 | $product_string = sprintf( |
| 471 | /* translators: 1: product title 2: product ID */ |
| 472 | esc_html__( '%1$s (#%2$s)', 'woocommerce' ), |
| 473 | $product->get_parent_id() ? $product->get_name() : $product->get_title(), |
| 474 | absint( $product->get_id() ) |
| 475 | ); |
| 476 | } |
| 477 | } |
| 478 | ?> |
| 479 | <select class="wc-product-search" name="customer_stock_notifications_product_filter" data-placeholder="<?php esc_attr_e( 'Select product…', 'woocommerce' ); ?>" data-allow_clear="true" id="customer_stock_notifications_product_filter"> |
| 480 | <?php if ( $product_string && $product_id ) { ?> |
| 481 | <option value="<?php echo esc_attr( $product_id ); ?>" selected="selected"><?php echo wp_kses_post( htmlspecialchars( $product_string, ENT_COMPAT ) ); ?></option> |
| 482 | <?php } ?> |
| 483 | </select> |
| 484 | <?php |
| 485 | } |
| 486 | |
| 487 | /** |
| 488 | * Display customer filter. |
| 489 | * |
| 490 | * @return void |
| 491 | */ |
| 492 | protected function display_customer_dropdown() { |
| 493 | $user_string = ''; |
| 494 | $user_id = ''; |
| 495 | |
| 496 | if ( ! empty( $_GET['customer_stock_notifications_customer_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 497 | $user_id = wc_clean( wp_unslash( $_GET['customer_stock_notifications_customer_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 498 | $user = get_user_by( 'id', absint( $user_id ) ); |
| 499 | |
| 500 | if ( $user ) { |
| 501 | $user_string = sprintf( |
| 502 | /* translators: 1: user display name 2: user ID 3: user email */ |
| 503 | esc_html__( '%1$s (#%2$s – %3$s)', 'woocommerce' ), |
| 504 | $user->display_name, |
| 505 | absint( $user->ID ), |
| 506 | $user->user_email |
| 507 | ); |
| 508 | } |
| 509 | } |
| 510 | ?> |
| 511 | <select class="wc-customer-search" name="customer_stock_notifications_customer_filter" data-placeholder="<?php esc_attr_e( 'Select customer…', 'woocommerce' ); ?>" data-allow_clear="true" id="customer_stock_notifications_customer_filter"> |
| 512 | <?php if ( $user_string && $user_id ) { ?> |
| 513 | <option value="<?php echo esc_attr( $user_id ); ?>" selected="selected"><?php echo wp_kses_post( htmlspecialchars( $user_string, ENT_COMPAT ) ); ?></option> |
| 514 | <?php } ?> |
| 515 | </select> |
| 516 | <?php |
| 517 | } |
| 518 | |
| 519 | /** |
| 520 | * Items of the `subsubsub` status menu. |
| 521 | * |
| 522 | * @return array |
| 523 | */ |
| 524 | protected function get_views() { |
| 525 | $status_links = array(); |
| 526 | |
| 527 | // All view. |
| 528 | $class = ! empty( $_REQUEST['status'] ) && 'all_customer_stock_notifications' === $_REQUEST['status'] ? 'current' : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 529 | $all_inner_html = sprintf( |
| 530 | /* translators: %s: Notifications count */ |
| 531 | _nx( |
| 532 | 'All <span class="count">(%s)</span>', |
| 533 | 'All <span class="count">(%s)</span>', |
| 534 | $this->total_items, |
| 535 | 'notifications_status', |
| 536 | 'woocommerce' |
| 537 | ), |
| 538 | number_format_i18n( $this->total_items ) |
| 539 | ); |
| 540 | |
| 541 | $status_links['all'] = $this->get_link( array( 'status' => 'all_customer_stock_notifications' ), $all_inner_html, $class ); |
| 542 | |
| 543 | // Active view. |
| 544 | $class = ! empty( $_REQUEST['status'] ) && 'active_customer_stock_notifications' === $_REQUEST['status'] ? 'current' : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 545 | $active_inner_html = sprintf( |
| 546 | /* translators: %s: Notifications count */ |
| 547 | _nx( |
| 548 | 'Active <span class="count">(%s)</span>', |
| 549 | 'Active <span class="count">(%s)</span>', |
| 550 | $this->total_active_items, |
| 551 | 'notifications_status', |
| 552 | 'woocommerce' |
| 553 | ), |
| 554 | number_format_i18n( $this->total_active_items ) |
| 555 | ); |
| 556 | |
| 557 | $status_links['active'] = $this->get_link( array( 'status' => 'active_customer_stock_notifications' ), $active_inner_html, $class ); |
| 558 | |
| 559 | // Sent view. |
| 560 | $class = ! empty( $_REQUEST['status'] ) && 'sent_customer_stock_notifications' === $_REQUEST['status'] ? 'current' : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 561 | $sent_inner_html = sprintf( |
| 562 | /* translators: %s: Notifications count */ |
| 563 | _nx( |
| 564 | 'Sent <span class="count">(%s)</span>', |
| 565 | 'Sent <span class="count">(%s)</span>', |
| 566 | $this->total_sent_items, |
| 567 | 'notifications_status', |
| 568 | 'woocommerce' |
| 569 | ), |
| 570 | number_format_i18n( $this->total_sent_items ) |
| 571 | ); |
| 572 | |
| 573 | $status_links['sent'] = $this->get_link( array( 'status' => 'sent_customer_stock_notifications' ), $sent_inner_html, $class ); |
| 574 | |
| 575 | // Cancelled view. |
| 576 | $class = ! empty( $_REQUEST['status'] ) && 'cancelled_customer_stock_notifications' === $_REQUEST['status'] ? 'current' : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 577 | $cancelled_inner_html = sprintf( |
| 578 | /* translators: %s: Notifications count */ |
| 579 | _nx( |
| 580 | 'Cancelled <span class="count">(%s)</span>', |
| 581 | 'Cancelled <span class="count">(%s)</span>', |
| 582 | $this->total_cancelled_items, |
| 583 | 'notifications_status', |
| 584 | 'woocommerce' |
| 585 | ), |
| 586 | number_format_i18n( $this->total_cancelled_items ) |
| 587 | ); |
| 588 | |
| 589 | $status_links['cancelled'] = $this->get_link( array( 'status' => 'cancelled_customer_stock_notifications' ), $cancelled_inner_html, $class ); |
| 590 | |
| 591 | // Pending view. |
| 592 | $class = ! empty( $_REQUEST['status'] ) && 'pending_customer_stock_notifications' === $_REQUEST['status'] ? 'current' : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 593 | $pending_inner_html = sprintf( |
| 594 | /* translators: %s: Notifications count */ |
| 595 | _nx( |
| 596 | 'Pending <span class="count">(%s)</span>', |
| 597 | 'Pending <span class="count">(%s)</span>', |
| 598 | $this->total_pending_items, |
| 599 | 'notifications_status', |
| 600 | 'woocommerce' |
| 601 | ), |
| 602 | number_format_i18n( $this->total_pending_items ) |
| 603 | ); |
| 604 | |
| 605 | $status_links['pending'] = $this->get_link( array( 'status' => 'pending_customer_stock_notifications' ), $pending_inner_html, $class ); |
| 606 | |
| 607 | return $status_links; |
| 608 | } |
| 609 | |
| 610 | /** |
| 611 | * Construct a link string from args. |
| 612 | * |
| 613 | * @param array $args Arguments for the link. |
| 614 | * @param string $label Link label. |
| 615 | * @param string $css_class CSS class. |
| 616 | * @return string |
| 617 | */ |
| 618 | protected function get_link( $args, $label, $css_class = '' ) { |
| 619 | $url = add_query_arg( $args ); |
| 620 | |
| 621 | $class_html = ''; |
| 622 | $aria_current = ''; |
| 623 | if ( ! empty( $css_class ) ) { |
| 624 | $class_html = sprintf( |
| 625 | ' class="%s"', |
| 626 | esc_attr( $css_class ) |
| 627 | ); |
| 628 | |
| 629 | if ( 'current' === $css_class ) { |
| 630 | $aria_current = ' aria-current="page"'; |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | return sprintf( |
| 635 | '<a href="%s"%s%s>%s</a>', |
| 636 | esc_url( $url ), |
| 637 | $class_html, |
| 638 | $aria_current, |
| 639 | $label |
| 640 | ); |
| 641 | } |
| 642 | |
| 643 | /** |
| 644 | * Display dates dropdown filter. |
| 645 | * |
| 646 | * @return void |
| 647 | */ |
| 648 | protected function display_months_dropdown() { |
| 649 | global $wp_locale; |
| 650 | |
| 651 | $months = $this->data_store->get_distinct_dates(); |
| 652 | |
| 653 | if ( ! is_array( $months ) ) { |
| 654 | return; |
| 655 | } |
| 656 | |
| 657 | $month_count = count( $months ); |
| 658 | |
| 659 | if ( $month_count < 1 ) { |
| 660 | return; |
| 661 | } |
| 662 | |
| 663 | $m = isset( $_GET['m'] ) ? (int) wp_unslash( $_GET['m'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 664 | ?> |
| 665 | <label for="filter-by-date" class="screen-reader-text"><?php esc_html_e( 'Filter by date', 'woocommerce' ); ?></label> |
| 666 | <select name="m" id="filter-by-date"> |
| 667 | <option<?php selected( $m, 0 ); ?> value="0"><?php esc_html_e( 'All dates', 'woocommerce' ); ?></option> |
| 668 | <?php |
| 669 | foreach ( $months as $arc_row ) { |
| 670 | if ( 0 === (int) $arc_row->year || 0 === (int) $arc_row->month ) { |
| 671 | continue; |
| 672 | } |
| 673 | |
| 674 | $month = zeroise( $arc_row->month, 2 ); |
| 675 | $year = $arc_row->year; |
| 676 | |
| 677 | printf( |
| 678 | "<option %s value='%s'>%s</option>\n", |
| 679 | selected( $m, $year . $month, false ), |
| 680 | esc_attr( $arc_row->year . $month ), |
| 681 | /* translators: %1$s: month %2$s: year */ |
| 682 | sprintf( esc_html__( '%1$s %2$d', 'woocommerce' ), esc_html( $wp_locale->get_month( $month ) ), esc_html( $year ) ) |
| 683 | ); |
| 684 | } |
| 685 | ?> |
| 686 | </select> |
| 687 | <?php |
| 688 | } |
| 689 | |
| 690 | /** |
| 691 | * Process actions. |
| 692 | */ |
| 693 | public function process_actions(): void { |
| 694 | $this->process_delete_action(); |
| 695 | $this->process_bulk_action(); |
| 696 | } |
| 697 | |
| 698 | /** |
| 699 | * Process delete action. |
| 700 | * |
| 701 | * @return void |
| 702 | */ |
| 703 | public function process_delete_action(): void { |
| 704 | |
| 705 | $action = isset( $_GET['notification_action'] ) ? wc_clean( wp_unslash( $_GET['notification_action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 706 | |
| 707 | if ( 'delete' !== $action ) { |
| 708 | return; |
| 709 | } |
| 710 | |
| 711 | $notification_id = isset( $_GET['notification_id'] ) ? absint( $_GET['notification_id'] ) : 0; |
| 712 | |
| 713 | if ( ! $notification_id ) { |
| 714 | return; |
| 715 | } |
| 716 | |
| 717 | check_admin_referer( 'delete_customer_stock_notification' ); |
| 718 | |
| 719 | try { |
| 720 | |
| 721 | $notification = Factory::get_notification( $notification_id ); |
| 722 | $this->data_store->delete( $notification ); |
| 723 | |
| 724 | $notice_message = __( 'Notification deleted.', 'woocommerce' ); |
| 725 | NotificationsPage::add_notice( $notice_message, 'success' ); |
| 726 | |
| 727 | } catch ( \Exception $e ) { |
| 728 | |
| 729 | $notice_message = __( 'Notification not found.', 'woocommerce' ); |
| 730 | NotificationsPage::add_notice( $notice_message, 'error' ); |
| 731 | } |
| 732 | |
| 733 | wp_safe_redirect( admin_url( NotificationsPage::PAGE_URL ) ); |
| 734 | exit(); |
| 735 | } |
| 736 | |
| 737 | /** |
| 738 | * Process bulk actions. |
| 739 | * |
| 740 | * @return void |
| 741 | */ |
| 742 | private function process_bulk_action() { |
| 743 | if ( ! $this->current_action() ) { |
| 744 | return; |
| 745 | } |
| 746 | |
| 747 | check_admin_referer( 'bulk-' . $this->_args['plural'] ); |
| 748 | |
| 749 | $notifications = isset( $_GET['notification'] ) && is_array( $_GET['notification'] ) ? array_map( 'absint', $_GET['notification'] ) : array(); |
| 750 | |
| 751 | if ( empty( $notifications ) ) { |
| 752 | return; |
| 753 | } |
| 754 | |
| 755 | $redirect_url = NotificationsPage::PAGE_URL; |
| 756 | |
| 757 | if ( 'enable' === $this->current_action() ) { |
| 758 | foreach ( $notifications as $id ) { |
| 759 | |
| 760 | $notification = Factory::get_notification( $id ); |
| 761 | $notification->set_status( NotificationStatus::ACTIVE ); |
| 762 | $this->data_store->update( $notification ); |
| 763 | |
| 764 | } |
| 765 | $notice_message = sprintf( |
| 766 | /* translators: %s: Notifications count */ |
| 767 | _nx( |
| 768 | '%s notification updated.', |
| 769 | '%s notifications updated.', |
| 770 | count( $notifications ), |
| 771 | 'notifications_status', |
| 772 | 'woocommerce' |
| 773 | ), |
| 774 | count( $notifications ) |
| 775 | ); |
| 776 | |
| 777 | NotificationsPage::add_notice( $notice_message, 'success' ); |
| 778 | |
| 779 | } elseif ( 'cancel' === $this->current_action() ) { |
| 780 | foreach ( $notifications as $id ) { |
| 781 | $notification = Factory::get_notification( $id ); |
| 782 | $notification->set_status( NotificationStatus::CANCELLED ); |
| 783 | $this->data_store->update( $notification ); |
| 784 | } |
| 785 | |
| 786 | $notice_message = sprintf( |
| 787 | /* translators: %s: Notifications count */ |
| 788 | _nx( |
| 789 | '%s notification updated.', |
| 790 | '%s notifications updated.', |
| 791 | count( $notifications ), |
| 792 | 'notifications_status', |
| 793 | 'woocommerce' |
| 794 | ), |
| 795 | count( $notifications ) |
| 796 | ); |
| 797 | |
| 798 | NotificationsPage::add_notice( $notice_message, 'success' ); |
| 799 | |
| 800 | } elseif ( 'delete' === $this->current_action() ) { |
| 801 | foreach ( $notifications as $id ) { |
| 802 | $notification = Factory::get_notification( $id ); |
| 803 | $this->data_store->delete( $notification ); |
| 804 | } |
| 805 | |
| 806 | $notice_message = sprintf( |
| 807 | /* translators: %s: Notifications count */ |
| 808 | _nx( |
| 809 | '%s notification deleted.', |
| 810 | '%s notifications deleted.', |
| 811 | count( $notifications ), |
| 812 | 'notifications_status', |
| 813 | 'woocommerce' |
| 814 | ), |
| 815 | count( $notifications ) |
| 816 | ); |
| 817 | |
| 818 | NotificationsPage::add_notice( $notice_message, 'success' ); |
| 819 | } |
| 820 | |
| 821 | wp_safe_redirect( $redirect_url ); |
| 822 | exit(); |
| 823 | } |
| 824 | } |
| 825 |