PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.0.1
WooCommerce v11.0.1
11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / class-wc-admin-attributes.php
woocommerce / includes / admin Last commit date
helper 3 weeks ago importers 1 month ago list-tables 1 month ago marketplace-suggestions 1 year ago meta-boxes 1 month ago notes 3 months ago plugin-updates 2 years ago reports 1 month ago settings 1 month ago views 1 month ago class-wc-admin-addons.php 10 months ago class-wc-admin-api-keys-table-list.php 3 years ago class-wc-admin-api-keys.php 1 month ago class-wc-admin-assets.php 1 month ago class-wc-admin-attributes.php 1 month ago class-wc-admin-brands.php 1 month ago class-wc-admin-customize.php 6 years ago class-wc-admin-dashboard-setup.php 1 month ago class-wc-admin-dashboard.php 1 month ago class-wc-admin-duplicate-product.php 1 month ago class-wc-admin-exporters.php 1 year ago class-wc-admin-help.php 2 years ago class-wc-admin-importers.php 1 year ago class-wc-admin-log-table-list.php 6 months ago class-wc-admin-marketplace-promotions.php 1 month ago class-wc-admin-menus.php 1 month ago class-wc-admin-meta-boxes.php 2 months ago class-wc-admin-notices.php 1 month ago class-wc-admin-permalink-settings.php 5 years ago class-wc-admin-pointers.php 3 years ago class-wc-admin-post-types.php 1 month ago class-wc-admin-profile.php 1 year ago class-wc-admin-reports.php 6 months ago class-wc-admin-settings.php 1 month ago class-wc-admin-setup-wizard.php 2 months ago class-wc-admin-status.php 1 year ago class-wc-admin-taxonomies.php 2 months ago class-wc-admin-upload-downloadable-product.php 2 years ago class-wc-admin-webhooks-table-list.php 2 months ago class-wc-admin-webhooks.php 1 month ago class-wc-admin.php 5 months ago wc-admin-functions.php 1 month ago wc-meta-box-functions.php 1 year ago woocommerce-legacy-reports.php 1 year ago
class-wc-admin-attributes.php
580 lines
1 <?php
2 /**
3 * Attributes Page
4 *
5 * The attributes section lets users add custom attributes to assign to products - they can also be used in the "Filter Products by Attribute" widget.
6 *
7 * @package WooCommerce\Admin
8 * @version 2.3.0
9 */
10
11 defined( 'ABSPATH' ) || exit;
12
13 /**
14 * WC_Admin_Attributes Class.
15 */
16 class WC_Admin_Attributes {
17
18 /**
19 * Edited attribute ID.
20 *
21 * @var int
22 */
23 private static $edited_attribute_id;
24
25 /**
26 * Handles output of the attributes page in admin.
27 *
28 * Shows the created attributes and lets you add new ones or edit existing ones.
29 * The added attributes are stored in the database and can be used for layered navigation.
30 */
31 public static function output() {
32 $result = '';
33 $action = '';
34
35 // Action to perform: add, edit, delete or none.
36 if ( ! empty( $_POST['add_new_attribute'] ) ) { // WPCS: CSRF ok.
37 $action = 'add';
38 } elseif ( ! empty( $_POST['save_attribute'] ) && ! empty( $_GET['edit'] ) ) { // WPCS: CSRF ok.
39 $action = 'edit';
40 } elseif ( ! empty( $_GET['delete'] ) ) {
41 $action = 'delete';
42 }
43
44 switch ( $action ) {
45 case 'add':
46 $result = self::process_add_attribute();
47 break;
48 case 'edit':
49 $result = self::process_edit_attribute();
50 break;
51 case 'delete':
52 $result = self::process_delete_attribute();
53 break;
54 }
55
56 if ( is_wp_error( $result ) ) {
57 echo '<div id="woocommerce_errors" class="error"><p>' . wp_kses_post( $result->get_error_message() ) . '</p></div>';
58 }
59
60 // Show admin interface.
61 if ( ! empty( $_GET['edit'] ) ) {
62 self::edit_attribute();
63 } else {
64 self::add_attribute();
65 }
66 }
67
68 /**
69 * Get and sanitize posted attribute data.
70 *
71 * @return array
72 */
73 private static function get_posted_attribute() {
74 $attribute = array(
75 'attribute_label' => isset( $_POST['attribute_label'] ) ? wc_clean( wp_unslash( $_POST['attribute_label'] ) ) : '', // WPCS: input var ok, CSRF ok.
76 'attribute_name' => isset( $_POST['attribute_name'] ) ? wc_sanitize_taxonomy_name( wp_unslash( $_POST['attribute_name'] ) ) : '', // WPCS: input var ok, CSRF ok, sanitization ok.
77 'attribute_type' => isset( $_POST['attribute_type'] ) ? wc_clean( wp_unslash( $_POST['attribute_type'] ) ) : 'select', // WPCS: input var ok, CSRF ok.
78 'attribute_orderby' => isset( $_POST['attribute_orderby'] ) ? wc_clean( wp_unslash( $_POST['attribute_orderby'] ) ) : '', // WPCS: input var ok, CSRF ok.
79 'attribute_public' => isset( $_POST['attribute_public'] ) ? 1 : 0, // WPCS: input var ok, CSRF ok.
80 );
81
82 if ( empty( $attribute['attribute_type'] ) ) {
83 $attribute['attribute_type'] = 'select';
84 }
85 if ( empty( $attribute['attribute_label'] ) ) {
86 $attribute['attribute_label'] = ucfirst( $attribute['attribute_name'] );
87 }
88 if ( empty( $attribute['attribute_name'] ) ) {
89 $attribute['attribute_name'] = wc_sanitize_taxonomy_name( $attribute['attribute_label'] );
90 }
91
92 return $attribute;
93 }
94
95 /**
96 * Add an attribute.
97 *
98 * @return bool|WP_Error
99 */
100 private static function process_add_attribute() {
101 check_admin_referer( 'woocommerce-add-new_attribute' );
102
103 $attribute = self::get_posted_attribute();
104 $args = array(
105 'name' => $attribute['attribute_label'],
106 'slug' => $attribute['attribute_name'],
107 'type' => $attribute['attribute_type'],
108 'order_by' => $attribute['attribute_orderby'],
109 'has_archives' => $attribute['attribute_public'],
110 );
111
112 $id = wc_create_attribute( $args );
113
114 if ( is_wp_error( $id ) ) {
115 return $id;
116 }
117
118 return true;
119 }
120
121 /**
122 * Edit an attribute.
123 *
124 * @return bool|WP_Error
125 */
126 private static function process_edit_attribute() {
127 $attribute_id = isset( $_GET['edit'] ) ? absint( $_GET['edit'] ) : 0;
128 check_admin_referer( 'woocommerce-save-attribute_' . $attribute_id );
129
130 $attribute = self::get_posted_attribute();
131 $args = array(
132 'name' => $attribute['attribute_label'],
133 'slug' => $attribute['attribute_name'],
134 'type' => $attribute['attribute_type'],
135 'order_by' => $attribute['attribute_orderby'],
136 'has_archives' => $attribute['attribute_public'],
137 );
138
139 $id = wc_update_attribute( $attribute_id, $args );
140
141 if ( is_wp_error( $id ) ) {
142 return $id;
143 }
144
145 self::$edited_attribute_id = $id;
146
147 return true;
148 }
149
150 /**
151 * Delete an attribute.
152 *
153 * @return bool
154 */
155 private static function process_delete_attribute() {
156 $attribute_id = isset( $_GET['delete'] ) ? absint( $_GET['delete'] ) : 0;
157 check_admin_referer( 'woocommerce-delete-attribute_' . $attribute_id );
158
159 return wc_delete_attribute( $attribute_id );
160 }
161
162 /**
163 * Output an inline script that shows a live UTF-8 byte count next to the
164 * slug input and visually warns when the value approaches or exceeds the
165 * server-side byte limit.
166 *
167 * The HTML `maxlength` attribute counts characters, not bytes, so a
168 * multibyte slug (e.g. Cyrillic, Chinese) can pass the browser's guard
169 * and still be rejected by `register_taxonomy()`'s 32-byte name limit.
170 * This counter closes that feedback gap before submission.
171 */
172 private static function slug_byte_counter_script(): void {
173 $max_bytes = wc_get_attribute_slug_max_byte_length();
174 /* translators: 1: current byte count, 2: maximum allowed bytes. */
175 $template = wp_json_encode( __( '%1$d / %2$d bytes', 'woocommerce' ), JSON_HEX_TAG | JSON_HEX_AMP );
176 if ( false === $template ) {
177 // Encoding the counter template failed (e.g. invalid UTF-8 in the translated
178 // string); skip the counter rather than emit broken inline JavaScript.
179 return;
180 }
181 ?>
182 <script type="text/javascript">
183 ( function() {
184 var input = document.getElementById( 'attribute_name' );
185 if ( ! input || ! ( 'TextEncoder' in window ) ) {
186 return;
187 }
188 var wrapper = input.closest( 'td, .form-field' );
189 var description = wrapper ? wrapper.querySelector( 'p.description' ) : null;
190 if ( ! description ) {
191 return;
192 }
193 var maxBytes = <?php echo (int) $max_bytes; ?>;
194 var template = <?php echo $template; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_json_encode with JSON_HEX_TAG produces JS-safe output. ?>;
195 var encoder = new TextEncoder();
196 var counter = document.createElement( 'span' );
197 counter.style.display = 'block';
198 counter.style.marginTop = '4px';
199 description.appendChild( counter );
200 function update() {
201 var bytes = encoder.encode( input.value ).length;
202 if ( 0 === bytes ) {
203 counter.textContent = '';
204 counter.style.color = '';
205 return;
206 }
207 counter.textContent = template
208 .replace( '%1$d', bytes )
209 .replace( '%2$d', maxBytes );
210 if ( bytes > maxBytes ) {
211 counter.style.color = '#d63638';
212 } else if ( bytes >= maxBytes - 3 ) {
213 // Within one multibyte character (up to 3 bytes) of the limit.
214 counter.style.color = '#996800';
215 } else {
216 counter.style.color = '';
217 }
218 }
219 input.addEventListener( 'input', update );
220 update();
221 } )();
222 </script>
223 <?php
224 }
225
226 /**
227 * Edit Attribute admin panel.
228 *
229 * Shows the interface for changing an attributes type between select and text.
230 */
231 public static function edit_attribute() {
232 global $wpdb;
233
234 $edit = isset( $_GET['edit'] ) ? absint( $_GET['edit'] ) : 0;
235
236 $attribute_to_edit = $wpdb->get_row(
237 $wpdb->prepare(
238 "
239 SELECT attribute_type, attribute_label, attribute_name, attribute_orderby, attribute_public
240 FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d
241 ",
242 $edit
243 )
244 );
245
246 ?>
247 <div class="wrap woocommerce">
248 <h1><?php esc_html_e( 'Edit attribute', 'woocommerce' ); ?></h1>
249
250 <?php
251 if ( ! $attribute_to_edit ) {
252 echo '<div id="woocommerce_errors" class="error"><p>' . esc_html__( 'Error: non-existing attribute ID.', 'woocommerce' ) . '</p></div>';
253 } else {
254 if ( self::$edited_attribute_id > 0 ) {
255 echo '<div id="message" class="updated"><p>' . esc_html__( 'Attribute updated successfully', 'woocommerce' ) . '</p><p><a href="' . esc_url( admin_url( 'edit.php?post_type=product&amp;page=product_attributes' ) ) . '">' . esc_html__( 'Back to Attributes', 'woocommerce' ) . '</a></p></div>';
256 self::$edited_attribute_id = null;
257 }
258 $att_type = $attribute_to_edit->attribute_type;
259 $att_label = format_to_edit( $attribute_to_edit->attribute_label );
260 $att_name = $attribute_to_edit->attribute_name;
261 $att_orderby = $attribute_to_edit->attribute_orderby;
262 $att_public = $attribute_to_edit->attribute_public;
263 ?>
264 <form action="edit.php?post_type=product&amp;page=product_attributes&amp;edit=<?php echo absint( $edit ); ?>" method="post">
265 <table class="form-table">
266 <tbody>
267 <?php do_action( 'woocommerce_before_edit_attribute_fields' ); ?>
268 <tr class="form-field form-required">
269 <th scope="row" valign="top">
270 <label for="attribute_label"><?php esc_html_e( 'Name', 'woocommerce' ); ?></label>
271 </th>
272 <td>
273 <input name="attribute_label" id="attribute_label" type="text" value="<?php echo esc_attr( $att_label ); ?>" />
274 <p class="description"><?php esc_html_e( 'Name for the attribute (shown on the front-end).', 'woocommerce' ); ?></p>
275 </td>
276 </tr>
277 <tr class="form-field form-required">
278 <th scope="row" valign="top">
279 <label for="attribute_name"><?php esc_html_e( 'Slug', 'woocommerce' ); ?></label>
280 </th>
281 <td>
282 <input name="attribute_name" id="attribute_name" type="text" value="<?php echo esc_attr( $att_name ); ?>" maxlength="29" />
283 <p class="description"><?php esc_html_e( 'Unique slug/reference for the attribute. May be up to 29 bytes; non-ASCII characters each count as 2–4 bytes.', 'woocommerce' ); ?></p>
284 </td>
285 </tr>
286 <tr class="form-field form-required">
287 <th scope="row" valign="top">
288 <label for="attribute_public"><?php esc_html_e( 'Enable archives?', 'woocommerce' ); ?></label>
289 </th>
290 <td>
291 <input name="attribute_public" id="attribute_public" type="checkbox" value="1" <?php checked( $att_public, 1 ); ?> />
292 <p class="description"><?php esc_html_e( 'Enable this if you want this attribute to have product archives in your store.', 'woocommerce' ); ?></p>
293 </td>
294 </tr>
295 <?php
296 /**
297 * Attribute types can change the way attributes are displayed on the frontend and admin.
298 *
299 * By Default WooCommerce only includes the `select` type. Others can be added with the
300 * `product_attributes_type_selector` filter. If there is only the default type registered,
301 * this setting will be hidden.
302 */
303 if ( wc_has_custom_attribute_types() ) {
304 ?>
305 <tr class="form-field form-required">
306 <th scope="row" valign="top">
307 <label for="attribute_type"><?php esc_html_e( 'Type', 'woocommerce' ); ?></label>
308 </th>
309 <td>
310 <select name="attribute_type" id="attribute_type">
311 <?php foreach ( wc_get_attribute_types() as $key => $value ) : ?>
312 <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $att_type, $key ); ?>><?php echo esc_html( $value ); ?></option>
313 <?php endforeach; ?>
314 <?php
315 /**
316 * Deprecated action in favor of product_attributes_type_selector filter.
317 *
318 * @todo Remove in 4.0.0
319 * @deprecated 2.4.0
320 */
321 do_action( 'woocommerce_admin_attribute_types' );
322 ?>
323 </select>
324 <p class="description"><?php esc_html_e( "Determines how this attribute's values are displayed.", 'woocommerce' ); ?></p>
325 </td>
326 </tr>
327 <?php
328 }//end if
329 ?>
330 <tr class="form-field form-required">
331 <th scope="row" valign="top">
332 <label for="attribute_orderby"><?php esc_html_e( 'Default sort order', 'woocommerce' ); ?></label>
333 </th>
334 <td>
335 <select name="attribute_orderby" id="attribute_orderby">
336 <option value="menu_order" <?php selected( $att_orderby, 'menu_order' ); ?>><?php esc_html_e( 'Custom ordering', 'woocommerce' ); ?></option>
337 <option value="name" <?php selected( $att_orderby, 'name' ); ?>><?php esc_html_e( 'Name', 'woocommerce' ); ?></option>
338 <option value="name_num" <?php selected( $att_orderby, 'name_num' ); ?>><?php esc_html_e( 'Name (numeric)', 'woocommerce' ); ?></option>
339 <option value="id" <?php selected( $att_orderby, 'id' ); ?>><?php esc_html_e( 'Term ID', 'woocommerce' ); ?></option>
340 </select>
341 <p class="description"><?php esc_html_e( 'Determines the sort order of the terms on the frontend shop product pages. If using custom ordering, you can drag and drop the terms in this attribute.', 'woocommerce' ); ?></p>
342 </td>
343 </tr>
344 <?php do_action( 'woocommerce_after_edit_attribute_fields' ); ?>
345 </tbody>
346 </table>
347 <p class="submit"><button type="submit" name="save_attribute" id="submit" class="button-primary" value="<?php esc_attr_e( 'Update', 'woocommerce' ); ?>"><?php esc_html_e( 'Update', 'woocommerce' ); ?></button></p>
348 <?php wp_nonce_field( 'woocommerce-save-attribute_' . $edit ); ?>
349 </form>
350 <?php self::slug_byte_counter_script(); ?>
351 <?php
352 }//end if
353 ?>
354 </div>
355 <?php
356 }
357
358 /**
359 * Add Attribute admin panel.
360 *
361 * Shows the interface for adding new attributes.
362 */
363 public static function add_attribute() {
364 ?>
365 <div class="wrap woocommerce">
366 <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
367
368 <br class="clear" />
369 <div id="col-container">
370 <div id="col-right">
371 <div class="col-wrap">
372 <table class="widefat attributes-table wp-list-table ui-sortable" style="width:100%">
373 <thead>
374 <tr>
375 <th scope="col"><?php esc_html_e( 'Name', 'woocommerce' ); ?></th>
376 <th scope="col"><?php esc_html_e( 'Slug', 'woocommerce' ); ?></th>
377 <?php if ( wc_has_custom_attribute_types() ) : ?>
378 <th scope="col"><?php esc_html_e( 'Type', 'woocommerce' ); ?></th>
379 <?php endif; ?>
380 <th scope="col"><?php esc_html_e( 'Order by', 'woocommerce' ); ?></th>
381 <th scope="col"><?php esc_html_e( 'Terms', 'woocommerce' ); ?></th>
382 </tr>
383 </thead>
384 <tbody>
385 <?php
386 $attribute_taxonomies = wc_get_attribute_taxonomies();
387 if ( $attribute_taxonomies ) {
388 /**
389 * Filters the maximum number of terms that will be displayed for each taxonomy in the Attributes page.
390 *
391 * @param int @default_max_terms_to_display Default value.
392 * @returns int Actual value to use, may be zero.
393 *
394 * @since 6.9.0
395 */
396 $max_terms_to_display = apply_filters( 'woocommerce_max_terms_displayed_in_attributes_page', 100 );
397 foreach ( $attribute_taxonomies as $tax ) :
398 ?>
399 <tr>
400 <td>
401 <strong><a href="edit-tags.php?taxonomy=<?php echo esc_attr( wc_attribute_taxonomy_name( $tax->attribute_name ) ); ?>&amp;post_type=product"><?php echo esc_html( $tax->attribute_label ); ?></a></strong>
402
403 <div class="row-actions"><span class="edit"><a href="<?php echo esc_url( add_query_arg( 'edit', $tax->attribute_id, 'edit.php?post_type=product&amp;page=product_attributes' ) ); ?>"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a> | </span><span class="delete"><a class="delete" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'delete', $tax->attribute_id, 'edit.php?post_type=product&amp;page=product_attributes' ), 'woocommerce-delete-attribute_' . $tax->attribute_id ) ); ?>"><?php esc_html_e( 'Delete', 'woocommerce' ); ?></a></span></div>
404 </td>
405 <td><?php echo esc_html( $tax->attribute_name ); ?></td>
406 <?php if ( wc_has_custom_attribute_types() ) : ?>
407 <td><?php echo esc_html( wc_get_attribute_type_label( $tax->attribute_type ) ); ?> <?php echo $tax->attribute_public ? esc_html__( '(Public)', 'woocommerce' ) : ''; ?></td>
408 <?php endif; ?>
409 <td>
410 <?php
411 switch ( $tax->attribute_orderby ) {
412 case 'name':
413 esc_html_e( 'Name', 'woocommerce' );
414 break;
415 case 'name_num':
416 esc_html_e( 'Name (numeric)', 'woocommerce' );
417 break;
418 case 'id':
419 esc_html_e( 'Term ID', 'woocommerce' );
420 break;
421 default:
422 esc_html_e( 'Custom ordering', 'woocommerce' );
423 break;
424 }
425 ?>
426 </td>
427 <td class="attribute-terms">
428 <?php
429 $taxonomy = wc_attribute_taxonomy_name( $tax->attribute_name );
430
431 if ( taxonomy_exists( $taxonomy ) ) {
432 $total_count = (int) get_terms(
433 array(
434 'taxonomy' => $taxonomy,
435 'fields' => 'count',
436 'hide_empty' => false,
437 )
438 );
439 if ( 0 === $total_count ) {
440 echo '<span class="na">&ndash;</span>';
441 } elseif ( $max_terms_to_display > 0 ) {
442 $terms = get_terms(
443 array(
444 'taxonomy' => $taxonomy,
445 'number' => $max_terms_to_display,
446 'fields' => 'names',
447 'hide_empty' => false,
448 )
449 );
450 $terms_string = implode( ', ', $terms );
451 if ( $total_count > $max_terms_to_display ) {
452 $remaining = $total_count - $max_terms_to_display;
453 /* translators: 1: Comma-separated terms list, 2: how many terms are hidden */
454 $terms_string = sprintf( __( '%1$s... (%2$s more)', 'woocommerce' ), $terms_string, $remaining );
455 }
456 echo esc_html( $terms_string );
457 } elseif ( 1 === $total_count ) {
458 echo esc_html( __( '1 term', 'woocommerce' ) );
459 } else {
460 /* translators: %s: Total count of terms available for the attribute */
461 echo esc_html( sprintf( __( '%s terms', 'woocommerce' ), $total_count ) );
462 }//end if
463 } else {
464 echo '<span class="na">&ndash;</span><br />';
465 }//end if
466 ?>
467 <br /><a href="edit-tags.php?taxonomy=<?php echo esc_attr( wc_attribute_taxonomy_name( $tax->attribute_name ) ); ?>&amp;post_type=product" class="configure-terms"><?php esc_html_e( 'Configure terms', 'woocommerce' ); ?></a>
468 </td>
469 </tr>
470 <?php
471 endforeach;
472 } else {
473 $column_count = wc_has_custom_attribute_types() ? '5' : '4';
474 ?>
475 <tr>
476 <td colspan="<?php echo esc_attr( $column_count ); ?>"><?php esc_html_e( 'No attributes currently exist.', 'woocommerce' ); ?></td>
477 </tr>
478 <?php
479 }//end if
480 ?>
481 </tbody>
482 </table>
483 </div>
484 </div>
485 <div id="col-left">
486 <div class="col-wrap">
487 <div class="form-wrap">
488 <h2><?php esc_html_e( 'Add new attribute', 'woocommerce' ); ?></h2>
489 <p><?php esc_html_e( 'Attributes let you define extra product data, such as size or color. You can use these attributes in the shop sidebar using the "layered nav" widgets.', 'woocommerce' ); ?></p>
490 <form action="edit.php?post_type=product&amp;page=product_attributes" method="post">
491 <?php do_action( 'woocommerce_before_add_attribute_fields' ); ?>
492
493 <div class="form-field">
494 <label for="attribute_label"><?php esc_html_e( 'Name', 'woocommerce' ); ?></label>
495 <input name="attribute_label" id="attribute_label" type="text" value="" />
496 <p class="description"><?php esc_html_e( 'Name for the attribute (shown on the front-end).', 'woocommerce' ); ?></p>
497 </div>
498
499 <div class="form-field">
500 <label for="attribute_name"><?php esc_html_e( 'Slug', 'woocommerce' ); ?></label>
501 <input name="attribute_name" id="attribute_name" type="text" value="" maxlength="29" />
502 <p class="description"><?php esc_html_e( 'Unique slug/reference for the attribute. May be up to 29 bytes; non-ASCII characters each count as 2–4 bytes.', 'woocommerce' ); ?></p>
503 </div>
504
505 <div class="form-field">
506 <label for="attribute_public"><input name="attribute_public" id="attribute_public" type="checkbox" value="1" /> <?php esc_html_e( 'Enable Archives?', 'woocommerce' ); ?></label>
507
508 <p class="description"><?php esc_html_e( 'Enable this if you want this attribute to have product archives in your store.', 'woocommerce' ); ?></p>
509 </div>
510
511 <?php
512 /**
513 * Attribute types can change the way attributes are displayed on the frontend and admin.
514 *
515 * By Default WooCommerce only includes the `select` type. Others can be added with the
516 * `product_attributes_type_selector` filter. If there is only the default type registered,
517 * this setting will be hidden.
518 */
519 if ( wc_has_custom_attribute_types() ) {
520 ?>
521 <div class="form-field">
522 <label for="attribute_type"><?php esc_html_e( 'Type', 'woocommerce' ); ?></label>
523 <select name="attribute_type" id="attribute_type">
524 <?php foreach ( wc_get_attribute_types() as $key => $value ) : ?>
525 <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $value ); ?></option>
526 <?php endforeach; ?>
527 <?php
528 /**
529 * Deprecated action in favor of product_attributes_type_selector filter.
530 *
531 * @todo Remove in 4.0.0
532 * @deprecated 2.4.0
533 */
534 do_action( 'woocommerce_admin_attribute_types' );
535 ?>
536 </select>
537 <p class="description"><?php esc_html_e( "Determines how this attribute's values are displayed.", 'woocommerce' ); ?></p>
538 </div>
539 <?php
540 }//end if
541 ?>
542
543 <div class="form-field">
544 <label for="attribute_orderby"><?php esc_html_e( 'Default sort order', 'woocommerce' ); ?></label>
545 <select name="attribute_orderby" id="attribute_orderby">
546 <option value="menu_order"><?php esc_html_e( 'Custom ordering', 'woocommerce' ); ?></option>
547 <option value="name"><?php esc_html_e( 'Name', 'woocommerce' ); ?></option>
548 <option value="name_num"><?php esc_html_e( 'Name (numeric)', 'woocommerce' ); ?></option>
549 <option value="id"><?php esc_html_e( 'Term ID', 'woocommerce' ); ?></option>
550 </select>
551 <p class="description"><?php esc_html_e( 'Determines the sort order of the terms on the frontend shop product pages. If using custom ordering, you can drag and drop the terms in this attribute.', 'woocommerce' ); ?></p>
552 </div>
553
554 <?php do_action( 'woocommerce_after_add_attribute_fields' ); ?>
555
556 <p class="submit"><button type="submit" name="add_new_attribute" id="submit" class="button button-primary" value="<?php esc_attr_e( 'Add attribute', 'woocommerce' ); ?>"><?php esc_html_e( 'Add attribute', 'woocommerce' ); ?></button></p>
557 <?php wp_nonce_field( 'woocommerce-add-new_attribute' ); ?>
558 </form>
559 </div>
560 </div>
561 </div>
562 </div>
563 <script type="text/javascript">
564 /* <![CDATA[ */
565
566 jQuery( 'a.delete' ).on( 'click', function() {
567 if ( window.confirm( '<?php esc_html_e( 'Are you sure you want to delete this attribute?', 'woocommerce' ); ?>' ) ) {
568 return true;
569 }
570 return false;
571 });
572
573 /* ]]> */
574 </script>
575 <?php self::slug_byte_counter_script(); ?>
576 </div>
577 <?php
578 }
579 }
580