builder
1 month ago
form-migrator
1 month ago
plugin-updates
8 years ago
settings
1 month ago
views
1 month ago
class-evf-admin-addons.php
4 months ago
class-evf-admin-assets.php
2 weeks ago
class-evf-admin-builder.php
2 months ago
class-evf-admin-dashboard.php
1 month ago
class-evf-admin-editor.php
4 years ago
class-evf-admin-embed-wizard.php
2 years ago
class-evf-admin-entries-table-list.php
2 months ago
class-evf-admin-entries.php
2 months ago
class-evf-admin-form-templates.php
1 month ago
class-evf-admin-forms-table-list.php
4 months ago
class-evf-admin-forms.php
4 months ago
class-evf-admin-import-export.php
1 month ago
class-evf-admin-menus.php
1 month ago
class-evf-admin-notices.php
4 months ago
class-evf-admin-preview-confirmation.php
1 year ago
class-evf-admin-settings.php
1 month ago
class-evf-admin-tools.php
2 months ago
class-evf-admin-welcome.php
2 years ago
class-evf-admin.php
2 months ago
class-evf-base-list-table.php
4 months ago
evf-admin-functions.php
1 month ago
class-evf-base-list-table.php
545 lines
| 1 | <?php |
| 2 | /** |
| 3 | * EverestForms Base List Table |
| 4 | * |
| 5 | * Abstract base class for all EverestForms table list classes. |
| 6 | * This class extends WP_List_Table and provides common functionality |
| 7 | * for both forms and entries table lists. |
| 8 | * |
| 9 | * @package EverestForms\Admin |
| 10 | * @since 3.4.3 |
| 11 | */ |
| 12 | |
| 13 | defined( 'ABSPATH' ) || exit; |
| 14 | |
| 15 | if ( ! class_exists( 'WP_List_Table' ) ) { |
| 16 | require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; |
| 17 | } |
| 18 | |
| 19 | /** |
| 20 | * Base table list class. |
| 21 | * |
| 22 | * This abstract class provides a foundation for all EverestForms table implementations. |
| 23 | * Child classes should extend this class and implement/override methods as needed. |
| 24 | * |
| 25 | * @abstract |
| 26 | */ |
| 27 | abstract class EVF_Base_List_Table extends WP_List_Table { |
| 28 | |
| 29 | /** |
| 30 | * Initialize the base table list. |
| 31 | * |
| 32 | * @param array $args An associative array of arguments. |
| 33 | */ |
| 34 | public function __construct( $args = array() ) { |
| 35 | parent::__construct( $args ); |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * No items found text. |
| 40 | * |
| 41 | * Default implementation. Child classes should override this method. |
| 42 | */ |
| 43 | public function no_items() { |
| 44 | esc_html_e( 'No items found.', 'everest-forms' ); |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * Process bulk actions. |
| 49 | * |
| 50 | * Default implementation. Child classes should override this method. |
| 51 | */ |
| 52 | public function process_bulk_action() { |
| 53 | // Child classes should implement their own bulk action logic. |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Get bulk actions. |
| 58 | * |
| 59 | * Default implementation. Child classes should override this method. |
| 60 | * |
| 61 | * @return array |
| 62 | */ |
| 63 | protected function get_bulk_actions() { |
| 64 | return array(); |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Get a list of sortable columns. |
| 69 | * |
| 70 | * Default implementation. Child classes can override this method. |
| 71 | * |
| 72 | * @return array |
| 73 | */ |
| 74 | protected function get_sortable_columns() { |
| 75 | return array(); |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Extra controls to be displayed between bulk actions and pagination. |
| 80 | * |
| 81 | * Default implementation. Child classes can override this method. |
| 82 | * |
| 83 | * @param string $which The location of the extra table nav markup. |
| 84 | */ |
| 85 | protected function extra_tablenav( $which ) { |
| 86 | // Child classes can implement their own extra tablenav logic. |
| 87 | } |
| 88 | |
| 89 | /** |
| 90 | * Display content above the table. |
| 91 | * |
| 92 | * This method can be overridden by child classes to add custom content |
| 93 | * above the table structure (before tablenav and the actual table). |
| 94 | * |
| 95 | * @since 3.4.3 |
| 96 | */ |
| 97 | protected function display_above_table() { |
| 98 | /** |
| 99 | * Hook to add content above the table. |
| 100 | * |
| 101 | * @param EVF_Base_List_Table $table The current table instance. |
| 102 | */ |
| 103 | do_action( 'everest_forms_above_table', $this ); |
| 104 | |
| 105 | // Default implementation - adds the everest-forms-extra-table-nav div |
| 106 | ?> |
| 107 | <div class="everest-forms-extra-table-nav"> |
| 108 | <?php |
| 109 | /** |
| 110 | * Hook to add content inside the extra table nav div. |
| 111 | * |
| 112 | * @param EVF_Base_List_Table $table The current table instance. |
| 113 | */ |
| 114 | do_action( 'everest_forms_extra_table_nav_content', $this ); |
| 115 | ?> |
| 116 | </div> |
| 117 | <?php |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Display the table with above-table content support. |
| 122 | * |
| 123 | * Overrides parent display() to add content above the table and wrap everything in a div. |
| 124 | */ |
| 125 | public function display() { |
| 126 | // Opening wrapper div |
| 127 | echo '<div class="everest-forms-table-wrapper">'; |
| 128 | |
| 129 | // Display content above the table. |
| 130 | $this->display_above_table(); |
| 131 | |
| 132 | $singular = $this->_args['singular']; |
| 133 | |
| 134 | $this->display_tablenav( 'top' ); |
| 135 | |
| 136 | $this->screen->render_screen_reader_content( 'heading_list' ); |
| 137 | ?> |
| 138 | <div class="everest-forms-table-container"> |
| 139 | <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>"> |
| 140 | <thead> |
| 141 | <tr> |
| 142 | <?php $this->print_column_headers(); ?> |
| 143 | </tr> |
| 144 | </thead> |
| 145 | |
| 146 | <tbody id="the-list" |
| 147 | <?php |
| 148 | if ( $singular ) { |
| 149 | echo " data-wp-lists='list:$singular'"; |
| 150 | } |
| 151 | ?> |
| 152 | > |
| 153 | <?php $this->display_rows_or_placeholder(); ?> |
| 154 | </tbody> |
| 155 | |
| 156 | <tfoot> |
| 157 | <tr> |
| 158 | <?php $this->print_column_headers( false ); ?> |
| 159 | </tr> |
| 160 | </tfoot> |
| 161 | |
| 162 | </table> |
| 163 | </div> |
| 164 | <?php |
| 165 | $this->display_tablenav( 'bottom' ); |
| 166 | |
| 167 | // Closing wrapper div |
| 168 | echo '</div>'; |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Table list views. |
| 173 | * |
| 174 | * Default implementation. Child classes can override this method. |
| 175 | * |
| 176 | * @return array |
| 177 | */ |
| 178 | protected function get_views() { |
| 179 | return array(); |
| 180 | } |
| 181 | |
| 182 | /** |
| 183 | * Column cb (checkbox). |
| 184 | * |
| 185 | * Default implementation for checkbox column. |
| 186 | * Child classes can override this method. |
| 187 | * |
| 188 | * @param object $item Item object. |
| 189 | * @return string |
| 190 | */ |
| 191 | public function column_cb( $item ) { |
| 192 | if ( ! isset( $item->ID ) ) { |
| 193 | return ''; |
| 194 | } |
| 195 | return sprintf( '<input type="checkbox" name="%1$s[]" value="%2$s" />', $this->_args['singular'], $item->ID ); |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Renders the columns. |
| 200 | * |
| 201 | * Default implementation for rendering columns. |
| 202 | * Child classes can override this method. |
| 203 | * |
| 204 | * @param object $item Item object. |
| 205 | * @param string $column_name Column Name. |
| 206 | * @return string |
| 207 | */ |
| 208 | public function column_default( $item, $column_name ) { |
| 209 | return ''; |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * Helper method to sanitize text field from request. |
| 214 | * |
| 215 | * @param string $key The request parameter key. |
| 216 | * @return string Sanitized value or empty string. |
| 217 | */ |
| 218 | protected function get_sanitized_request( $key ) { |
| 219 | return isset( $_REQUEST[ $key ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $key ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification |
| 220 | } |
| 221 | |
| 222 | /** |
| 223 | * Helper method to get integer value from request. |
| 224 | * |
| 225 | * @param string $key The request parameter key. |
| 226 | * @return int Sanitized integer value or 0. |
| 227 | */ |
| 228 | protected function get_int_request( $key ) { |
| 229 | return isset( $_REQUEST[ $key ] ) ? absint( $_REQUEST[ $key ] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * Helper method to check if a request parameter exists. |
| 234 | * |
| 235 | * @param string $key The request parameter key. |
| 236 | * @return bool True if parameter exists, false otherwise. |
| 237 | */ |
| 238 | protected function has_request( $key ) { |
| 239 | return isset( $_REQUEST[ $key ] ); // phpcs:ignore WordPress.Security.NonceVerification |
| 240 | } |
| 241 | |
| 242 | /** |
| 243 | * Get a list of CSS classes for the table element. |
| 244 | * |
| 245 | * @return array List of CSS class names. |
| 246 | */ |
| 247 | protected function get_table_classes() { |
| 248 | $classes = parent::get_table_classes(); |
| 249 | $classes[] = 'evf-base-list-table'; |
| 250 | return $classes; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Get the current URL with sanitized server variables. |
| 255 | * |
| 256 | * @return string Current URL. |
| 257 | */ |
| 258 | protected function get_current_url() { |
| 259 | $http_host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; |
| 260 | $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; |
| 261 | |
| 262 | if ( empty( $http_host ) || empty( $request_uri ) ) { |
| 263 | return admin_url(); |
| 264 | } |
| 265 | |
| 266 | $current_url = set_url_scheme( 'http://' . $http_host . $request_uri ); |
| 267 | $removable_query_args = wp_removable_query_args(); |
| 268 | |
| 269 | return remove_query_arg( $removable_query_args, $current_url ); |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * Get the display text for pagination showing current range of items. |
| 274 | * |
| 275 | * @param int $current Current page number. |
| 276 | * @param int $per_page Number of items per page. |
| 277 | * @param int $total_items Total number of items. |
| 278 | * @return string HTML output for displaying item range. |
| 279 | */ |
| 280 | protected function get_pagination_display_text( $current, $per_page, $total_items ) { |
| 281 | $start = ( ( $current - 1 ) * $per_page ) + 1; |
| 282 | $end = min( $current * $per_page, $total_items ); |
| 283 | |
| 284 | return '<span class="displaying-num">' . sprintf( |
| 285 | /* translators: 1: start number, 2: end number, 3: total number */ |
| 286 | esc_html__( 'Showing %1$s-%2$s of %3$s entries', 'everest-forms' ), |
| 287 | number_format_i18n( $start ), |
| 288 | number_format_i18n( $end ), |
| 289 | number_format_i18n( $total_items ) |
| 290 | ) . '</span>'; |
| 291 | } |
| 292 | |
| 293 | /** |
| 294 | * Get first and last page navigation buttons. |
| 295 | * |
| 296 | * @param int $current Current page number. |
| 297 | * @param int $total_pages Total number of pages. |
| 298 | * @param string $current_url Current URL without pagination args. |
| 299 | * @return array Array of HTML strings for navigation buttons. |
| 300 | */ |
| 301 | protected function get_first_last_buttons( $current, $total_pages, $current_url ) { |
| 302 | $buttons = array(); |
| 303 | |
| 304 | // First page button. |
| 305 | if ( 1 === $current ) { |
| 306 | $buttons['first'] = '<span class="tablenav-pages-navspan button disabled first-page" aria-hidden="true" title="' . esc_attr__( 'First page', 'everest-forms' ) . '">«</span>'; |
| 307 | } else { |
| 308 | $buttons['first'] = sprintf( |
| 309 | "<a class='first-page button' href='%s' title='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
| 310 | esc_url( add_query_arg( 'paged', 1, $current_url ) ), |
| 311 | esc_attr__( 'First page', 'everest-forms' ), |
| 312 | esc_html__( 'First page', 'everest-forms' ), |
| 313 | '«' |
| 314 | ); |
| 315 | } |
| 316 | |
| 317 | // Last page button. |
| 318 | if ( $total_pages === $current ) { |
| 319 | $buttons['last'] = '<span class="tablenav-pages-navspan button disabled last-page" aria-hidden="true" title="' . esc_attr__( 'Last page', 'everest-forms' ) . '">»</span>'; |
| 320 | } else { |
| 321 | $buttons['last'] = sprintf( |
| 322 | "<a class='last-page button' href='%s' title='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
| 323 | esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), |
| 324 | esc_attr__( 'Last page', 'everest-forms' ), |
| 325 | esc_html__( 'Last page', 'everest-forms' ), |
| 326 | '»' |
| 327 | ); |
| 328 | } |
| 329 | |
| 330 | return $buttons; |
| 331 | } |
| 332 | |
| 333 | /** |
| 334 | * Get previous and next page navigation buttons. |
| 335 | * |
| 336 | * @param int $current Current page number. |
| 337 | * @param int $total_pages Total number of pages. |
| 338 | * @param string $current_url Current URL without pagination args. |
| 339 | * @return array Array of HTML strings for navigation buttons. |
| 340 | */ |
| 341 | protected function get_prev_next_buttons( $current, $total_pages, $current_url ) { |
| 342 | $buttons = array(); |
| 343 | |
| 344 | // Previous page button. |
| 345 | if ( 1 === $current ) { |
| 346 | $buttons['prev'] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">‹</span>'; |
| 347 | } else { |
| 348 | $buttons['prev'] = sprintf( |
| 349 | "<a class='prev-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
| 350 | esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ), |
| 351 | esc_html__( 'Previous page', 'everest-forms' ), |
| 352 | '‹' |
| 353 | ); |
| 354 | } |
| 355 | |
| 356 | // Next page button. |
| 357 | if ( $total_pages === $current ) { |
| 358 | $buttons['next'] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">›</span>'; |
| 359 | } else { |
| 360 | $buttons['next'] = sprintf( |
| 361 | "<a class='next-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
| 362 | esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ), |
| 363 | esc_html__( 'Next page', 'everest-forms' ), |
| 364 | '›' |
| 365 | ); |
| 366 | } |
| 367 | |
| 368 | return $buttons; |
| 369 | } |
| 370 | |
| 371 | /** |
| 372 | * Get numbered page links for pagination. |
| 373 | * |
| 374 | * @param int $current Current page number. |
| 375 | * @param int $total_pages Total number of pages. |
| 376 | * @param string $current_url Current URL without pagination args. |
| 377 | * @return array Array of HTML strings for page number links. |
| 378 | */ |
| 379 | protected function get_page_number_links( $current, $total_pages, $current_url ) { |
| 380 | $page_links = array(); |
| 381 | $mid_size = 2; |
| 382 | $end_size = 1; |
| 383 | |
| 384 | for ( $n = 1; $n <= $total_pages; $n++ ) { |
| 385 | $show_link = false; |
| 386 | |
| 387 | // Show first and last pages. |
| 388 | if ( $n <= $end_size || $n > $total_pages - $end_size ) { |
| 389 | $show_link = true; |
| 390 | } |
| 391 | |
| 392 | // Show pages around current page. |
| 393 | if ( $n >= $current - $mid_size && $n <= $current + $mid_size ) { |
| 394 | $show_link = true; |
| 395 | } |
| 396 | |
| 397 | if ( $show_link ) { |
| 398 | if ( $n === $current ) { |
| 399 | $page_links[] = sprintf( |
| 400 | "<span class='page-numbers current' aria-current='page'>%s</span>", |
| 401 | number_format_i18n( $n ) |
| 402 | ); |
| 403 | } else { |
| 404 | $page_links[] = sprintf( |
| 405 | "<a class='page-numbers' href='%s' aria-label='%s'>%s</a>", |
| 406 | esc_url( add_query_arg( 'paged', $n, $current_url ) ), |
| 407 | /* translators: %s: page number */ |
| 408 | esc_attr( sprintf( __( 'Page %s', 'everest-forms' ), number_format_i18n( $n ) ) ), |
| 409 | number_format_i18n( $n ) |
| 410 | ); |
| 411 | } |
| 412 | } elseif ( ! empty( $page_links ) && strpos( $page_links[ count( $page_links ) - 1 ], 'dots' ) === false ) { |
| 413 | $page_links[] = '<span class="page-numbers dots" aria-hidden="true">...</span>'; |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | return $page_links; |
| 418 | } |
| 419 | |
| 420 | /** |
| 421 | * Display the pagination. |
| 422 | * |
| 423 | * Enhanced pagination with numbered page links, first/last buttons, and items per page selector. |
| 424 | * Only displays at the bottom of the table. |
| 425 | * |
| 426 | * @param string $which The location of the pagination nav. |
| 427 | */ |
| 428 | protected function pagination( $which ) { |
| 429 | if ( 'top' === $which ) { |
| 430 | return; |
| 431 | } |
| 432 | |
| 433 | if ( empty( $this->_pagination_args ) ) { |
| 434 | return; |
| 435 | } |
| 436 | |
| 437 | $total_items = $this->_pagination_args['total_items']; |
| 438 | $total_pages = $this->_pagination_args['total_pages']; |
| 439 | $infinite_scroll = false; |
| 440 | |
| 441 | if ( isset( $this->_pagination_args['infinite_scroll'] ) ) { |
| 442 | $infinite_scroll = $this->_pagination_args['infinite_scroll']; |
| 443 | } |
| 444 | |
| 445 | $current = $this->get_pagenum(); |
| 446 | $per_page = $this->_pagination_args['per_page']; |
| 447 | $current_url = $this->get_current_url(); |
| 448 | |
| 449 | // Get display text showing current range. |
| 450 | $output = $this->get_pagination_display_text( $current, $per_page, $total_items ); |
| 451 | |
| 452 | // Build page links array. |
| 453 | $page_links = array(); |
| 454 | |
| 455 | // Get first/last buttons. |
| 456 | $first_last = $this->get_first_last_buttons( $current, $total_pages, $current_url ); |
| 457 | $page_links[] = $first_last['first']; |
| 458 | |
| 459 | // Get prev/next buttons. |
| 460 | $prev_next = $this->get_prev_next_buttons( $current, $total_pages, $current_url ); |
| 461 | $page_links[] = $prev_next['prev']; |
| 462 | |
| 463 | // Get numbered page links. |
| 464 | $number_links = $this->get_page_number_links( $current, $total_pages, $current_url ); |
| 465 | $page_links = array_merge( $page_links, $number_links ); |
| 466 | |
| 467 | // Add next and last buttons. |
| 468 | $page_links[] = $prev_next['next']; |
| 469 | $page_links[] = $first_last['last']; |
| 470 | |
| 471 | $pagination_links_class = 'pagination-links'; |
| 472 | if ( ! empty( $infinite_scroll ) ) { |
| 473 | $pagination_links_class .= ' hide-if-js'; |
| 474 | } |
| 475 | $output .= "\n<span class='$pagination_links_class'>" . implode( "\n", $page_links ) . '</span>'; |
| 476 | |
| 477 | if ( $total_pages ) { |
| 478 | $page_class = $total_pages < 2 ? ' one-page' : ''; |
| 479 | } else { |
| 480 | $page_class = ' no-pages'; |
| 481 | } |
| 482 | $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>"; |
| 483 | |
| 484 | echo $this->_pagination; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 485 | } |
| 486 | |
| 487 | /** |
| 488 | * Get current page number. |
| 489 | * |
| 490 | * @return int Current page number. |
| 491 | */ |
| 492 | public function get_pagenum() { |
| 493 | $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification |
| 494 | |
| 495 | if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) { |
| 496 | $pagenum = $this->_pagination_args['total_pages']; |
| 497 | } |
| 498 | |
| 499 | return max( 1, $pagenum ); |
| 500 | } |
| 501 | |
| 502 | /** |
| 503 | * Display the search box. |
| 504 | * |
| 505 | * @param string $text The 'submit' button label. |
| 506 | * @param string $input_id ID attribute value for the search input field. |
| 507 | */ |
| 508 | public function search_box( $text, $input_id ) { |
| 509 | if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) { |
| 510 | return; |
| 511 | } |
| 512 | |
| 513 | $input_id = $input_id . '-search-input'; |
| 514 | |
| 515 | if ( ! empty( $_REQUEST['orderby'] ) ) { |
| 516 | echo '<input type="hidden" name="orderby" value="' . esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ) ) . '" />'; |
| 517 | } |
| 518 | if ( ! empty( $_REQUEST['order'] ) ) { |
| 519 | echo '<input type="hidden" name="order" value="' . esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ) ) . '" />'; |
| 520 | } |
| 521 | ?> |
| 522 | <div class="search-box evf-search"> |
| 523 | <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"> |
| 524 | <?php echo esc_html( $text ); ?>: |
| 525 | </label> |
| 526 | |
| 527 | <input type="search" |
| 528 | id="<?php echo esc_attr( $input_id ); ?>" |
| 529 | name="s" |
| 530 | value="<?php _admin_search_query(); ?>" |
| 531 | placeholder="<?php echo esc_attr( $text ); ?>" /> |
| 532 | |
| 533 | <button type="submit" id="search-submit" class="evf-search-submit button"> |
| 534 | <span class="screen-reader-text"><?php echo esc_html( $text ); ?></span> |
| 535 | <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 24 24"> |
| 536 | <path fill="currentColor" fill-rule="evenodd" |
| 537 | d="M4 11a7 7 0 1 1 12.042 4.856 1.012 1.012 0 0 0-.186.186A7 7 0 0 1 4 11Zm12.618 7.032a9 9 0 1 1 1.414-1.414l3.675 3.675a1 1 0 0 1-1.414 1.414l-3.675-3.675Z" |
| 538 | clip-rule="evenodd"></path> |
| 539 | </svg> |
| 540 | </button> |
| 541 | </div> |
| 542 | <?php |
| 543 | } |
| 544 | } |
| 545 |