__( 'rule', 'aicoso-dynamic-pricing-for-woocommerce' ), 'plural' => __( 'rules', 'aicoso-dynamic-pricing-for-woocommerce' ), 'ajax' => false, ) ); } /** * Get safe table name for SQL queries. * * @param string $table Base table name. * @return string Full table name with prefix. */ private function get_table_name( $table ) { global $wpdb; return $wpdb->prefix . $table; } /** * Helper method for cached database queries. * * @param string $cache_key The cache key. * @param callable $query_func The query function to execute if cache miss. * @param int $cache_expire Cache expiration time in seconds (default: 300). * @return mixed The query result. */ private function cached_query( $cache_key, $query_func, $cache_expire = 300 ) { $result = wp_cache_get( $cache_key, 'dp_aicoso_rules_list' ); if ( false === $result ) { $result = call_user_func( $query_func ); wp_cache_set( $cache_key, $result, 'dp_aicoso_rules_list', $cache_expire ); } return $result; } /** * Get table columns. * * @return array */ public function get_columns() { return array( 'cb' => '', 'rule_name' => __( 'Rule Name', 'aicoso-dynamic-pricing-for-woocommerce' ), 'rule_type' => __( 'Type', 'aicoso-dynamic-pricing-for-woocommerce' ), 'priority' => __( 'Priority', 'aicoso-dynamic-pricing-for-woocommerce' ), 'status' => __( 'Status', 'aicoso-dynamic-pricing-for-woocommerce' ), 'applies_to' => __( 'Applies To', 'aicoso-dynamic-pricing-for-woocommerce' ), 'discount' => __( 'Discount', 'aicoso-dynamic-pricing-for-woocommerce' ), 'date_range' => __( 'Date Range', 'aicoso-dynamic-pricing-for-woocommerce' ), 'actions' => __( 'Actions', 'aicoso-dynamic-pricing-for-woocommerce' ), ); } /** * Get sortable columns. * * @return array */ public function get_sortable_columns() { return array( 'rule_name' => array( 'rule_name', false ), 'rule_type' => array( 'rule_type', false ), 'priority' => array( 'priority', true ), 'status' => array( 'rule_status', false ), ); } /** * Get bulk actions. * * @return array */ public function get_bulk_actions() { return array( 'activate' => __( 'Activate', 'aicoso-dynamic-pricing-for-woocommerce' ), 'deactivate' => __( 'Deactivate', 'aicoso-dynamic-pricing-for-woocommerce' ), 'delete' => __( 'Delete', 'aicoso-dynamic-pricing-for-woocommerce' ), 'duplicate' => __( 'Duplicate', 'aicoso-dynamic-pricing-for-woocommerce' ), ); } /** * Prepare items for display. */ public function prepare_items() { global $wpdb; $per_page = $this->get_items_per_page( 'dp_aicoso_rules_per_page', 20 ); $current_page = $this->get_pagenum(); // Set column headers. $this->_column_headers = array( $this->get_columns(), array(), // Hidden columns. $this->get_sortable_columns(), ); // Process bulk actions. $this->process_bulk_action(); // Prepare query. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP_List_Table handles nonce verification. $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_sql_orderby( wp_unslash( $_REQUEST['orderby'] ) ) : 'priority'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP_List_Table handles nonce verification. $order = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ) : 'DESC'; $offset = ( $current_page - 1 ) * $per_page; // Get total items with caching. $total_items = $this->cached_query( 'dp_aicoso_rules_total_count', function () use ( $wpdb ) { $table_name = $this->get_table_name( 'dp_aicoso_rules' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Query is cached via cached_query(), table name is safe. return $wpdb->get_var( 'SELECT COUNT(*) FROM ' . esc_sql( $table_name ) ); } ); // Get items with caching. $this->items = $this->cached_query( 'dp_aicoso_rules_page_' . $current_page . '_orderby_' . $orderby . '_order_' . $order . '_per_' . $per_page, function () use ( $wpdb, $orderby, $order, $per_page, $offset ) { $table_name = $this->get_table_name( 'dp_aicoso_rules' ); // Define allowed ORDER BY clauses to avoid any variable interpolation. $allowed_order_clauses = array( 'id ASC', 'id DESC', 'rule_name ASC', 'rule_name DESC', 'rule_type ASC', 'rule_type DESC', 'priority ASC', 'priority DESC', 'status ASC', 'status DESC', 'created_at ASC', 'created_at DESC', ); $order_clause = $orderby . ' ' . $order; if ( ! in_array( $order_clause, $allowed_order_clauses, true ) ) { $order_clause = 'priority DESC'; // Safe default. } // Use fixed SQL queries with no variable interpolation - switch based on validated clause. switch ( $order_clause ) { case 'id ASC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY rule_id ASC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'id DESC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY rule_id DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'rule_name ASC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY rule_name ASC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'rule_name DESC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY rule_name DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'rule_type ASC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY rule_type ASC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'rule_type DESC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY rule_type DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'priority ASC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY priority ASC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'priority DESC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY priority DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'status ASC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY status ASC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'status DESC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY status DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'created_at ASC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY created_at ASC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); case 'created_at DESC': // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY created_at DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); default: // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached, table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'dp_aicoso_rules ORDER BY priority DESC LIMIT %d OFFSET %d', $per_page, $offset ), ARRAY_A ); } }, 60 // Short cache for list table data. ); // Set pagination. $this->set_pagination_args( array( 'total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil( $total_items / $per_page ), ) ); } /** * Process bulk actions. */ private function process_bulk_action() { // Security check. if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'bulk-' . $this->_args['plural'] ) ) { return; } $action = $this->current_action(); if ( ! $action ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already verified above. $rule_ids = isset( $_POST['rule'] ) ? array_map( 'intval', wp_unslash( $_POST['rule'] ) ) : array(); if ( empty( $rule_ids ) ) { return; } global $wpdb; $table_name = $this->get_table_name( 'dp_aicoso_rules' ); switch ( $action ) { case 'delete': foreach ( $rule_ids as $rule_id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation $wpdb->delete( $table_name, array( 'rule_id' => $rule_id ), array( '%d' ) ); } // Clear cache after delete. break; case 'activate': foreach ( $rule_ids as $rule_id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation $wpdb->update( $table_name, array( 'rule_status' => 'active' ), array( 'rule_id' => $rule_id ), array( '%s' ), array( '%d' ) ); } // Clear cache after activate. wp_cache_flush_group( 'dp_aicoso_rules_list' ); break; case 'deactivate': foreach ( $rule_ids as $rule_id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation $wpdb->update( $table_name, array( 'rule_status' => 'inactive' ), array( 'rule_id' => $rule_id ), array( '%s' ), array( '%d' ) ); } // Clear cache after deactivate. wp_cache_flush_group( 'dp_aicoso_rules_list' ); break; case 'duplicate': foreach ( $rule_ids as $rule_id ) { $this->duplicate_rule( $rule_id ); } // Clear cache after duplicate. wp_cache_flush_group( 'dp_aicoso_rules_list' ); break; } // Clear price calculator cache after bulk actions. $calculator = DP_AICOSO_Price_Calculator::get_instance(); $calculator->clear_cache(); } /** * Duplicate a rule. * * @param int $rule_id Rule ID to duplicate. */ private function duplicate_rule( $rule_id ) { global $wpdb; $table_name = $this->get_table_name( 'dp_aicoso_rules' ); // Get original rule with caching. $rule = $this->cached_query( 'dp_aicoso_rule_' . $rule_id, function () use ( $wpdb, $table_name, $rule_id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Query is cached via cached_query(), table name is safe. return $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . esc_sql( $table_name ) . ' WHERE rule_id = %d', $rule_id ), ARRAY_A ); } ); if ( ! $rule ) { return; } // Remove ID and modify name. unset( $rule['rule_id'] ); $rule['rule_name'] = $rule['rule_name'] . ' (Copy)'; $rule['rule_status'] = 'inactive'; // Start as inactive. // Insert duplicate. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation. $wpdb->insert( $table_name, $rule ); // Duplicate relationships if needed. $new_rule_id = $wpdb->insert_id; if ( $new_rule_id ) { $this->duplicate_rule_relationships( $rule_id, $new_rule_id ); // Clear cache after duplicate. wp_cache_flush_group( 'dp_aicoso_rules_list' ); } } /** * Duplicate rule relationships. * * @param int $old_rule_id Original rule ID. * @param int $new_rule_id New rule ID. */ private function duplicate_rule_relationships( $old_rule_id, $new_rule_id ) { global $wpdb; $relationships_table = $this->get_table_name( 'dp_aicoso_rule_relationships' ); // Get relationships with caching. $relationships = $this->cached_query( 'dp_aicoso_rule_relationships_' . $old_rule_id, function () use ( $wpdb, $relationships_table, $old_rule_id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Query is cached via cached_query(), table name is safe. return $wpdb->get_results( $wpdb->prepare( 'SELECT object_id, object_type FROM ' . esc_sql( $relationships_table ) . ' WHERE rule_id = %d', $old_rule_id ), ARRAY_A ); } ); foreach ( $relationships as $relationship ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation $wpdb->insert( $relationships_table, array( 'rule_id' => $new_rule_id, 'object_id' => $relationship['object_id'], 'object_type' => $relationship['object_type'], ) ); } } /** * Default column display. * * @param array $item Item data. * @param string $column_name Column name. * @return string */ public function column_default( $item, $column_name ) { switch ( $column_name ) { case 'priority': return intval( $item['priority'] ); case 'date_range': return $this->format_date_range( $item ); default: return isset( $item[ $column_name ] ) ? $item[ $column_name ] : ''; } } /** * Checkbox column. * * @param array $item Item data. * @return string */ public function column_cb( $item ) { return sprintf( '', $item['rule_id'] ); } /** * Rule name column. * * @param array $item Item data. * @return string */ public function column_rule_name( $item ) { $edit_url = admin_url( 'admin.php?page=dp-aicoso-add-rule&rule_id=' . $item['rule_id'] ); $actions = array( 'edit' => sprintf( '%s', $edit_url, __( 'Edit', 'aicoso-dynamic-pricing-for-woocommerce' ) ), 'duplicate' => sprintf( '%s', $item['rule_id'], __( 'Duplicate', 'aicoso-dynamic-pricing-for-woocommerce' ) ), 'delete' => sprintf( '%s', $item['rule_id'], __( 'Delete', 'aicoso-dynamic-pricing-for-woocommerce' ) ), ); return sprintf( '%s %s', $edit_url, esc_html( $item['rule_name'] ), $this->row_actions( $actions ) ); } /** * Rule type column. * * @param array $item Item data. * @return string */ public function column_rule_type( $item ) { $types = array( 'product' => __( 'Product', 'aicoso-dynamic-pricing-for-woocommerce' ), 'category' => __( 'Category', 'aicoso-dynamic-pricing-for-woocommerce' ), 'role' => __( 'User Role', 'aicoso-dynamic-pricing-for-woocommerce' ), 'time' => __( 'Time-based', 'aicoso-dynamic-pricing-for-woocommerce' ), 'custom' => __( 'Custom', 'aicoso-dynamic-pricing-for-woocommerce' ), ); $type = isset( $types[ $item['rule_type'] ] ) ? $types[ $item['rule_type'] ] : $item['rule_type']; return sprintf( '%s', esc_attr( $item['rule_type'] ), esc_html( $type ) ); } /** * Status column. * * @param array $item Item data. * @return string */ public function column_status( $item ) { $status_labels = array( 'active' => __( 'Active', 'aicoso-dynamic-pricing-for-woocommerce' ), 'inactive' => __( 'Inactive', 'aicoso-dynamic-pricing-for-woocommerce' ), 'scheduled' => __( 'Scheduled', 'aicoso-dynamic-pricing-for-woocommerce' ), ); $status = isset( $status_labels[ $item['rule_status'] ] ) ? $status_labels[ $item['rule_status'] ] : $item['rule_status']; return sprintf( '%s', esc_attr( $item['rule_status'] ), esc_html( $status ) ); } /** * Applies to column. * * @param array $item Item data. * @return string */ public function column_applies_to( $item ) { global $wpdb; $relationships_table = $this->get_table_name( 'dp_aicoso_rule_relationships' ); // Get relationships with caching. $relationships = $this->cached_query( 'dp_aicoso_rule_applies_to_' . $item['rule_id'], function () use ( $wpdb, $relationships_table, $item ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is cached via cached_query(). Table name is safely escaped with esc_sql(). return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . esc_sql( $relationships_table ) . ' WHERE rule_id = %d', $item['rule_id'] ) ); } ); if ( empty( $relationships ) ) { return __( 'All products', 'aicoso-dynamic-pricing-for-woocommerce' ); } $applies_to = array(); foreach ( $relationships as $rel ) { if ( 'product' === $rel->object_type ) { $product = wc_get_product( $rel->object_id ); if ( $product ) { $applies_to[] = $product->get_name(); } } elseif ( 'category' === $rel->object_type ) { $term = get_term( $rel->object_id, 'product_cat' ); if ( $term && ! is_wp_error( $term ) ) { $applies_to[] = $term->name . ' (Category)'; } } elseif ( 0 === strpos( $rel->object_type, 'role:' ) ) { $role = str_replace( 'role:', '', $rel->object_type ); $applies_to[] = ucfirst( $role ) . ' (Role)'; } } return ! empty( $applies_to ) ? implode( ', ', $applies_to ) : __( 'Various', 'aicoso-dynamic-pricing-for-woocommerce' ); } /** * Discount column. * * @param array $item Item data. * @return string */ public function column_discount( $item ) { $adjustments = ! empty( $item['adjustments'] ) ? json_decode( $item['adjustments'], true ) : array(); if ( empty( $adjustments ) ) { return '-'; } // Check for quantity ranges first. if ( ! empty( $adjustments['quantity_ranges'] ) && is_array( $adjustments['quantity_ranges'] ) ) { $ranges = $adjustments['quantity_ranges']; $range_count = count( $ranges ); // Find min and max discounts for display. $min_discount = null; $max_discount = null; $discount_type = null; foreach ( $ranges as $range ) { if ( isset( $range['discount_value'] ) && '' !== $range['discount_value'] ) { $value = floatval( $range['discount_value'] ); $type = isset( $range['discount_type'] ) ? $range['discount_type'] : 'percentage'; if ( null === $discount_type ) { $discount_type = $type; } if ( null === $min_discount || $value < $min_discount ) { $min_discount = $value; } if ( null === $max_discount || $value > $max_discount ) { $max_discount = $value; } } } // Format the display. if ( null !== $min_discount && null !== $max_discount ) { if ( $min_discount == $max_discount ) { // All ranges have same discount. if ( 'percentage' === $discount_type ) { /* translators: %1$s: Discount percentage, %2$d: Number of tiers */ return sprintf( __( '%1$s%% (%2$d tiers)', 'aicoso-dynamic-pricing-for-woocommerce' ), $min_discount, $range_count ); } else { /* translators: %1$s: Price amount, %2$d: Number of tiers */ return sprintf( __( '%1$s (%2$d tiers)', 'aicoso-dynamic-pricing-for-woocommerce' ), wc_price( $min_discount ), $range_count ); } } elseif ( 'percentage' === $discount_type ) { // Different discounts in ranges. /* translators: %1$s: Min discount, %2$s: Max discount, %3$d: Number of tiers */ return sprintf( __( '%1$s-%2$s%% (%3$d tiers)', 'aicoso-dynamic-pricing-for-woocommerce' ), $min_discount, $max_discount, $range_count ); } else { /* translators: %1$s: Min price, %2$s: Max price, %3$d: Number of tiers */ return sprintf( __( '%1$s-%2$s (%3$d tiers)', 'aicoso-dynamic-pricing-for-woocommerce' ), wc_price( $min_discount ), wc_price( $max_discount ), $range_count ); } } // If we couldn't determine discounts from ranges, show tier count. /* translators: %d: number of quantity tiers */ return sprintf( __( '%d quantity tiers', 'aicoso-dynamic-pricing-for-woocommerce' ), $range_count ); } // Check for new array format [{'type': 'percentage', 'value': 40}]. if ( isset( $adjustments[0] ) && is_array( $adjustments[0] ) ) { $first_adjustment = $adjustments[0]; if ( isset( $first_adjustment['type'] ) && isset( $first_adjustment['value'] ) ) { $type = $first_adjustment['type']; $value = $first_adjustment['value']; if ( 'percentage' === $type ) { return $value . '%'; } elseif ( 'fixed' === $type ) { return wc_price( $value ); } elseif ( 'fixed_price' === $type ) { return wc_price( $value ) . ' ' . __( '(Fixed)', 'aicoso-dynamic-pricing-for-woocommerce' ); } // If value is 0 or empty, still show it. if ( 0 == $value ) { if ( 'percentage' === $type ) { return '0%'; } else { return wc_price( 0 ); } } } } // Try old format for backward compatibility. if ( isset( $adjustments['discount_type'] ) && isset( $adjustments['discount_value'] ) ) { if ( 'percentage' === $adjustments['discount_type'] ) { return $adjustments['discount_value'] . '%'; } elseif ( 'fixed' === $adjustments['discount_type'] ) { return wc_price( $adjustments['discount_value'] ); } elseif ( 'fixed_price' === $adjustments['discount_type'] ) { return wc_price( $adjustments['discount_value'] ) . ' ' . __( '(Fixed)', 'aicoso-dynamic-pricing-for-woocommerce' ); } } // Check for default adjustment. if ( isset( $adjustments['default'] ) ) { $default = $adjustments['default']; if ( 'percentage' === $default['type'] ) { return $default['value'] . '%'; } elseif ( 'fixed' === $default['type'] ) { return wc_price( $default['value'] ); } } return __( 'Variable', 'aicoso-dynamic-pricing-for-woocommerce' ); } /** * Actions column. * * @param array $item Item data. * @return string */ public function column_actions( $item ) { $actions = array(); // Toggle status button. if ( 'active' === $item['rule_status'] ) { $actions[] = sprintf( '', $item['rule_id'], __( 'Deactivate', 'aicoso-dynamic-pricing-for-woocommerce' ) ); } else { $actions[] = sprintf( '', $item['rule_id'], __( 'Activate', 'aicoso-dynamic-pricing-for-woocommerce' ) ); } // Edit button. $actions[] = sprintf( '%s', admin_url( 'admin.php?page=dp-aicoso-add-rule&rule_id=' . $item['rule_id'] ), __( 'Edit', 'aicoso-dynamic-pricing-for-woocommerce' ) ); return implode( ' ', $actions ); } /** * Format date range. * * @param array $item Item data. * @return string */ private function format_date_range( $item ) { $start = ! empty( $item['start_date'] ) ? $item['start_date'] : null; $end = ! empty( $item['end_date'] ) ? $item['end_date'] : null; if ( ! $start && ! $end ) { return __( 'Always', 'aicoso-dynamic-pricing-for-woocommerce' ); } $output = ''; if ( $start ) { $output .= date_i18n( get_option( 'date_format' ), strtotime( $start ) ); } else { $output .= __( 'Now', 'aicoso-dynamic-pricing-for-woocommerce' ); } $output .= ' - '; if ( $end ) { $output .= date_i18n( get_option( 'date_format' ), strtotime( $end ) ); } else { $output .= __( 'Forever', 'aicoso-dynamic-pricing-for-woocommerce' ); } return $output; } /** * Display when no items found. */ public function no_items() { esc_html_e( 'No pricing rules found. Create your first rule to get started!', 'aicoso-dynamic-pricing-for-woocommerce' ); } }