Visual Form Builder - Version 3.0.5

Version Description

Download this release

Release Info

Developer mmuro
Plugin Icon 128x128 Visual Form Builder
Version 3.0.5
Comparing to
See all releases

Code changes from version 3.0.4 to 3.0.5

admin/class-admin-menu.php CHANGED
@@ -188,11 +188,10 @@ class Visual_Form_Builder_Admin_Menu {
188
 
189
  <h3><?php _e( 'Promote Visual Form Builder' , 'visual-form-builder'); ?></h3>
190
  <ul id="promote-vfb">
191
- <li id="twitter"><?php _e( 'Follow VFB Pro on Twitter' , 'visual-form-builder'); ?>: <a href="http://twitter.com/#!/vfbpro">@vfbpro</a></li>
192
- <li id="star"><a href="http://wordpress.org/extend/plugins/visual-form-builder/"><?php _e( 'Rate Visual Form Builder on WordPress.org' , 'visual-form-builder'); ?></a></li>
193
- <li id="paypal">
194
- <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=G87A9UN9CLPH4&lc=US&item_name=Visual%20Form%20Builder&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" width="74" height="21"></a>
195
- </li>
196
  </ul>
197
  </div> <!-- #vfb-upgrade-column -->
198
  </div> <!-- #vfb-sidebar -->
188
 
189
  <h3><?php _e( 'Promote Visual Form Builder' , 'visual-form-builder'); ?></h3>
190
  <ul id="promote-vfb">
191
+ <li id="star"><a href="http://wordpress.org/extend/plugins/visual-form-builder/"><?php _e( 'Rate Visual Form Builder on WordPress.org' , 'visual-form-builder'); ?></a></li>
192
+ <li id="paypal">
193
+ <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=G87A9UN9CLPH4&lc=US&item_name=Visual%20Form%20Builder&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" width="74" height="21"></a>
194
+ </li>
 
195
  </ul>
196
  </div> <!-- #vfb-upgrade-column -->
197
  </div> <!-- #vfb-sidebar -->
admin/class-ajax.php CHANGED
@@ -66,7 +66,7 @@ class Visual_Form_Builder_Admin_AJAX {
66
 
67
  $form_id = absint( $data['form_id'] );
68
  $field_key = sanitize_title( $_POST['field_type'] );
69
- $field_name = esc_html( $_POST['field_type'] );
70
  $field_type = strtolower( sanitize_title( $_POST['field_type'] ) );
71
 
72
  // Set defaults for validation
66
 
67
  $form_id = absint( $data['form_id'] );
68
  $field_key = sanitize_title( $_POST['field_type'] );
69
+ $field_name = sanitize_text_field( $_POST['field_type'] );
70
  $field_type = strtolower( sanitize_title( $_POST['field_type'] ) );
71
 
72
  // Set defaults for validation
admin/class-entries-detail.php CHANGED
@@ -19,8 +19,6 @@ class Visual_Form_Builder_Entries_Detail {
19
 
20
  $entries = $wpdb->get_results( $wpdb->prepare( "SELECT forms.form_title, entries.* FROM " . VFB_WP_FORMS_TABLE_NAME . " AS forms INNER JOIN " . VFB_WP_ENTRIES_TABLE_NAME . " AS entries ON entries.form_id = forms.form_id WHERE entries.entries_id = %d", $entry_id ) );
21
 
22
- echo '<p>' . sprintf( '<a href="?page=%s" class="view-entry">&laquo; Back to Entries</a>', $_GET['page'] ) . '</p>';
23
-
24
  // Get the date/time format that is saved in the options table
25
  $date_format = get_option('date_format');
26
  $time_format = get_option('time_format');
@@ -30,7 +28,7 @@ class Visual_Form_Builder_Entries_Detail {
30
  $data = unserialize( $entry->data );
31
  ?>
32
  <form id="entry-edit" method="post" action="">
33
- <h3><span><?php echo stripslashes( $entry->form_title ); ?> : <?php echo __( 'Entry' , 'visual-form-builder'); ?> # <?php echo $entry->entries_id; ?></span></h3>
34
  <div id="vfb-poststuff" class="metabox-holder has-right-sidebar">
35
  <div id="side-info-column" class="inner-sidebar">
36
  <div id="side-sortables">
@@ -41,25 +39,25 @@ class Visual_Form_Builder_Entries_Detail {
41
  <div id="minor-publishing">
42
  <div id="misc-publishing-actions">
43
  <div class="misc-pub-section">
44
- <span><strong><?php echo __( 'Form Title' , 'visual-form-builder'); ?>: </strong><?php echo stripslashes( $entry->form_title ); ?></span>
45
  </div>
46
  <div class="misc-pub-section">
47
- <span><strong><?php echo __( 'Date Submitted' , 'visual-form-builder'); ?>: </strong><?php echo date( "$date_format $time_format", strtotime( $entry->date_submitted ) ); ?></span>
48
  </div>
49
  <div class="misc-pub-section">
50
- <span><strong><?php echo __( 'IP Address' , 'visual-form-builder'); ?>: </strong><?php echo $entry->ip_address; ?></span>
51
  </div>
52
  <div class="misc-pub-section">
53
- <span><strong><?php echo __( 'Email Subject' , 'visual-form-builder'); ?>: </strong><?php echo stripslashes( $entry->subject ); ?></span>
54
  </div>
55
  <div class="misc-pub-section">
56
- <span><strong><?php echo __( 'Sender Name' , 'visual-form-builder'); ?>: </strong><?php echo stripslashes( $entry->sender_name ); ?></span>
57
  </div>
58
  <div class="misc-pub-section">
59
- <span><strong><?php echo __( 'Sender Email' , 'visual-form-builder'); ?>: </strong><a href="mailto:<?php echo stripslashes( $entry->sender_email ); ?>"><?php echo stripslashes( $entry->sender_email ); ?></a></span>
60
  </div>
61
  <div class="misc-pub-section">
62
- <span><strong><?php echo __( 'Emailed To' , 'visual-form-builder'); ?>: </strong><?php echo preg_replace('/\b([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})\b/i', '<a href="mailto:$1">$1</a>', implode( ',', unserialize( stripslashes( $entry->emails_to ) ) ) ); ?></span>
63
  </div>
64
  <div class="clear"></div>
65
  </div> <!--#misc-publishing-actions -->
@@ -67,7 +65,7 @@ class Visual_Form_Builder_Entries_Detail {
67
 
68
  <div id="major-publishing-actions">
69
  <div id="delete-action">
70
- <?php echo sprintf( '<a class="submitdelete deletion entry-delete" href="?page=%2$s&action=%3$s&entry=%4$d">%1$s</a>', __( 'Move to Trash', 'visual-form-builder' ), $_GET['page'], 'trash', $entry_id ); ?>
71
  </div>
72
  <div id="publishing-action">
73
  <?php submit_button( __( 'Print', 'visual-form-builder' ), 'secondary', 'submit', false, array( 'onclick' => 'window.print();return false;' ) ); ?>
19
 
20
  $entries = $wpdb->get_results( $wpdb->prepare( "SELECT forms.form_title, entries.* FROM " . VFB_WP_FORMS_TABLE_NAME . " AS forms INNER JOIN " . VFB_WP_ENTRIES_TABLE_NAME . " AS entries ON entries.form_id = forms.form_id WHERE entries.entries_id = %d", $entry_id ) );
21
 
 
 
22
  // Get the date/time format that is saved in the options table
23
  $date_format = get_option('date_format');
24
  $time_format = get_option('time_format');
28
  $data = unserialize( $entry->data );
29
  ?>
30
  <form id="entry-edit" method="post" action="">
31
+ <h3><span><?php echo stripslashes( $entry->form_title ); ?> : <?php _e( 'Entry' , 'visual-form-builder'); ?> # <?php echo $entry->entries_id; ?></span></h3>
32
  <div id="vfb-poststuff" class="metabox-holder has-right-sidebar">
33
  <div id="side-info-column" class="inner-sidebar">
34
  <div id="side-sortables">
39
  <div id="minor-publishing">
40
  <div id="misc-publishing-actions">
41
  <div class="misc-pub-section">
42
+ <span><strong><?php _e( 'Form Title' , 'visual-form-builder'); ?>: </strong><?php echo esc_html( $entry->form_title ); ?></span>
43
  </div>
44
  <div class="misc-pub-section">
45
+ <span><strong><?php _e( 'Date Submitted' , 'visual-form-builder'); ?>: </strong><?php echo esc_html( date( "$date_format $time_format", strtotime( $entry->date_submitted ) ) ); ?></span>
46
  </div>
47
  <div class="misc-pub-section">
48
+ <span><strong><?php _e( 'IP Address' , 'visual-form-builder'); ?>: </strong><?php echo esc_html( $entry->ip_address ); ?></span>
49
  </div>
50
  <div class="misc-pub-section">
51
+ <span><strong><?php _e( 'Email Subject' , 'visual-form-builder'); ?>: </strong><?php echo esc_html( $entry->subject ); ?></span>
52
  </div>
53
  <div class="misc-pub-section">
54
+ <span><strong><?php _e( 'Sender Name' , 'visual-form-builder'); ?>: </strong><?php echo esc_html( $entry->sender_name ); ?></span>
55
  </div>
56
  <div class="misc-pub-section">
57
+ <span><strong><?php _e( 'Sender Email' , 'visual-form-builder'); ?>: </strong><a href="mailto:<?php echo esc_html( $entry->sender_email ); ?>"><?php echo esc_html( $entry->sender_email ); ?></a></span>
58
  </div>
59
  <div class="misc-pub-section">
60
+ <span><strong><?php _e( 'Emailed To' , 'visual-form-builder'); ?>: </strong><?php echo preg_replace('/\b([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})\b/i', '<a href="mailto:$1">$1</a>', esc_html( implode( ',', unserialize( stripslashes( $entry->emails_to ) ) ) ) ); ?></span>
61
  </div>
62
  <div class="clear"></div>
63
  </div> <!--#misc-publishing-actions -->
65
 
66
  <div id="major-publishing-actions">
67
  <div id="delete-action">
68
+ <?php echo sprintf( '<a class="submitdelete deletion entry-delete" href="%2$s&action=%3$s&entry=%4$d">%1$s</a>', __( 'Move to Trash', 'visual-form-builder' ), admin_url( 'admin.php?page=vfb-entries' ), 'trash', $entry_id ); ?>
69
  </div>
70
  <div id="publishing-action">
71
  <?php submit_button( __( 'Print', 'visual-form-builder' ), 'secondary', 'submit', false, array( 'onclick' => 'window.print();return false;' ) ); ?>
admin/class-entries-list.php CHANGED
@@ -53,13 +53,13 @@ class Visual_Form_Builder_Entries_List extends Visual_Form_Builder_List_Table {
53
 
54
  // Build row actions
55
  if ( !$this->get_entry_status() || 'all' == $this->get_entry_status() )
56
- $actions['view'] = sprintf( '<a href="?page=%s&action=%s&entry=%s" id="%3$s" class="view-entry">View</a>', $_GET['page'], 'view', $item['entry_id'] );
57
 
58
  if ( !$this->get_entry_status() || 'all' == $this->get_entry_status() )
59
- $actions['trash'] = sprintf( '<a href="?page=%s&action=%s&entry=%s">Trash</a>', $_GET['page'], 'trash', $item['entry_id'] );
60
  elseif ( $this->get_entry_status() && 'trash' == $this->get_entry_status() ) {
61
- $actions['restore'] = sprintf( '<a href="?page=%s&action=%s&entry=%s">%s</a>', $_GET['page'], 'restore', $item['entry_id'], __( 'Restore', 'visual-form-builder' ) );
62
- $actions['delete'] = sprintf( '<a href="?page=%s&action=%s&entry=%s">%s</a>', $_GET['page'], 'delete', $item['entry_id'], __( 'Delete Permanently', 'visual-form-builder' ) );
63
  }
64
 
65
  return sprintf( '%1$s %2$s', $item['form'], $this->row_actions( $actions ) );
53
 
54
  // Build row actions
55
  if ( !$this->get_entry_status() || 'all' == $this->get_entry_status() )
56
+ $actions['view'] = sprintf( '<a href="%s&action=%s&entry=%s" id="%3$s" class="view-entry">View</a>', admin_url( 'admin.php?page=vfb-entries' ), 'view', $item['entry_id'] );
57
 
58
  if ( !$this->get_entry_status() || 'all' == $this->get_entry_status() )
59
+ $actions['trash'] = sprintf( '<a href="%s&action=%s&entry=%s">Trash</a>', admin_url( 'admin.php?page=vfb-entries' ), 'trash', $item['entry_id'] );
60
  elseif ( $this->get_entry_status() && 'trash' == $this->get_entry_status() ) {
61
+ $actions['restore'] = sprintf( '<a href="%s&action=%s&entry=%s">%s</a>', admin_url( 'admin.php?page=vfb-entries' ), 'restore', $item['entry_id'], __( 'Restore', 'visual-form-builder' ) );
62
+ $actions['delete'] = sprintf( '<a href="%s&action=%s&entry=%s">%s</a>', admin_url( 'admin.php?page=vfb-entries' ), 'delete', $item['entry_id'], __( 'Delete Permanently', 'visual-form-builder' ) );
63
  }
64
 
65
  return sprintf( '%1$s %2$s', $item['form'], $this->row_actions( $actions ) );
admin/class-forms-list.php CHANGED
@@ -51,8 +51,8 @@ class Visual_Form_Builder_Forms_List extends Visual_Form_Builder_List_Table {
51
  $actions = array();
52
 
53
  // Edit Form
54
- $form_title = sprintf( '<strong><a href="?page=%s&action=%s&form=%s" id="%3$s" class="view-form">%s</a></strong>', $_GET['page'], 'edit', $item['form_id'], $item['form_title'] );
55
- $actions['edit'] = sprintf( '<a href="?page=%s&action=%s&form=%s" id="%3$s" class="view-form">%s</a>', $_GET['page'], 'edit', $item['form_id'], __( 'Edit', 'visual-form-builder' ) );
56
 
57
  // Duplicate Form
58
  $actions['copy'] = sprintf( '<a href="%s&action=%s&form=%s" id="%3$s" class="view-form">%s</a>', wp_nonce_url( admin_url( 'admin.php?page=visual-form-builder' ), 'copy-form-' . $item['form_id'] ), 'copy_form', $item['form_id'], __( 'Duplicate', 'visual-form-builder' ) );
51
  $actions = array();
52
 
53
  // Edit Form
54
+ $form_title = sprintf( '<strong><a href="%s&action=%s&form=%s" id="%3$s" class="view-form">%s</a></strong>', admin_url( 'admin.php?page=visual-form-builder' ), 'edit', $item['form_id'], $item['form_title'] );
55
+ $actions['edit'] = sprintf( '<a href="%s&action=%s&form=%s" id="%3$s" class="view-form">%s</a>', admin_url( 'admin.php?page=visual-form-builder' ), 'edit', $item['form_id'], __( 'Edit', 'visual-form-builder' ) );
56
 
57
  // Duplicate Form
58
  $actions['copy'] = sprintf( '<a href="%s&action=%s&form=%s" id="%3$s" class="view-form">%s</a>', wp_nonce_url( admin_url( 'admin.php?page=visual-form-builder' ), 'copy-form-' . $item['form_id'] ), 'copy_form', $item['form_id'], __( 'Duplicate', 'visual-form-builder' ) );
admin/class-media-button.php CHANGED
@@ -36,7 +36,7 @@ class Visual_Form_Builder_Media_Button {
36
  wp_nonce_url( admin_url( 'admin-ajax.php' ), 'vfb_media_button' )
37
  );
38
  ?>
39
- <a href="<?php echo esc_url( $button_url ); ?>" class="button add_media thickbox" title="<?php _e( 'Add Visual Form Builder form', 'visual-form-builder' ); ?>">
40
  <span class="dashicons dashicons-feedback" style="color:#888; display: inline-block; width: 18px; height: 18px; vertical-align: text-top; margin: 0 4px 0 0;"></span>
41
  <?php _e( 'Add Form', 'visual-form-builder' ); ?>
42
  </a>
@@ -78,7 +78,7 @@ class Visual_Form_Builder_Media_Button {
78
  <p><?php _e( 'Select a form below to insert into any Post or Page.', 'visual-form-builder' ); ?></p>
79
  <select id="vfb_forms" name="vfb_forms">
80
  <?php foreach( $forms as $form ) : ?>
81
- <option value="<?php echo $form->form_id; ?>"><?php echo $form->form_title; ?></option>
82
  <?php endforeach; ?>
83
  </select>
84
  <?php
36
  wp_nonce_url( admin_url( 'admin-ajax.php' ), 'vfb_media_button' )
37
  );
38
  ?>
39
+ <a href="<?php echo esc_url( $button_url ); ?>" class="button add_media thickbox" title="<?php esc_attr_e( 'Add Visual Form Builder form', 'visual-form-builder' ); ?>">
40
  <span class="dashicons dashicons-feedback" style="color:#888; display: inline-block; width: 18px; height: 18px; vertical-align: text-top; margin: 0 4px 0 0;"></span>
41
  <?php _e( 'Add Form', 'visual-form-builder' ); ?>
42
  </a>
78
  <p><?php _e( 'Select a form below to insert into any Post or Page.', 'visual-form-builder' ); ?></p>
79
  <select id="vfb_forms" name="vfb_forms">
80
  <?php foreach( $forms as $form ) : ?>
81
+ <option value="<?php echo esc_attr( $form->form_id ); ?>"><?php echo $form->form_title; ?></option>
82
  <?php endforeach; ?>
83
  </select>
84
  <?php
inc/class-list-table.php CHANGED
@@ -16,7 +16,6 @@ class Visual_Form_Builder_List_Table {
16
  *
17
  * @since 3.1.0
18
  * @var array
19
- * @access public
20
  */
21
  public $items;
22
 
@@ -25,7 +24,6 @@ class Visual_Form_Builder_List_Table {
25
  *
26
  * @since 3.1.0
27
  * @var array
28
- * @access protected
29
  */
30
  protected $_args;
31
 
@@ -41,8 +39,7 @@ class Visual_Form_Builder_List_Table {
41
  * The current screen.
42
  *
43
  * @since 3.1.0
44
- * @var object
45
- * @access protected
46
  */
47
  protected $screen;
48
 
@@ -51,7 +48,6 @@ class Visual_Form_Builder_List_Table {
51
  *
52
  * @since 3.1.0
53
  * @var array
54
- * @access private
55
  */
56
  private $_actions;
57
 
@@ -60,7 +56,6 @@ class Visual_Form_Builder_List_Table {
60
  *
61
  * @since 3.1.0
62
  * @var string
63
- * @access private
64
  */
65
  private $_pagination;
66
 
@@ -69,31 +64,47 @@ class Visual_Form_Builder_List_Table {
69
  *
70
  * @since 4.1.0
71
  * @var array
72
- * @access protected
73
  */
74
  protected $modes = array();
75
 
76
  /**
77
- * Stores the value returned by ->get_column_info()
78
  *
 
79
  * @var array
80
  */
81
  protected $_column_headers;
82
 
83
  /**
84
- * [protected description]
 
85
  * @var array
86
  */
87
  protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
88
 
89
  /**
90
- * [protected description]
 
91
  * @var array
92
  */
93
- protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
94
- 'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination',
95
- 'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
96
- 'single_row_columns' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  /**
99
  * Constructor.
@@ -102,7 +113,6 @@ class Visual_Form_Builder_List_Table {
102
  * the default $args.
103
  *
104
  * @since 3.1.0
105
- * @access public
106
  *
107
  * @param array|string $args {
108
  * Array or string of arguments.
@@ -112,31 +122,35 @@ class Visual_Form_Builder_List_Table {
112
  * in the list table, e.g. 'posts'. Default empty.
113
  * @type string $singular Singular label for an object being listed, e.g. 'post'.
114
  * Default empty
115
- * @type bool $ajax Whether the list table supports AJAX. This includes loading
116
  * and sorting data, for example. If true, the class will call
117
- * the {@see _js_vars()} method in the footer to provide variables
118
- * to any scripts handling AJAX events. Default false.
119
  * @type string $screen String containing the hook name used to determine the current
120
  * screen. If left null, the current screen will be automatically set.
121
  * Default null.
122
  * }
123
  */
124
  public function __construct( $args = array() ) {
125
- $args = wp_parse_args( $args, array(
126
- 'plural' => '',
127
- 'singular' => '',
128
- 'ajax' => false,
129
- 'screen' => null,
130
- ) );
 
 
 
131
 
132
  $this->screen = convert_to_screen( $args['screen'] );
133
 
134
  add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
135
 
136
- if ( !$args['plural'] )
137
  $args['plural'] = $this->screen->base;
 
138
 
139
- $args['plural'] = sanitize_key( $args['plural'] );
140
  $args['singular'] = sanitize_key( $args['singular'] );
141
 
142
  $this->_args = $args;
@@ -148,85 +162,80 @@ class Visual_Form_Builder_List_Table {
148
 
149
  if ( empty( $this->modes ) ) {
150
  $this->modes = array(
151
- 'list' => __( 'List View' ),
152
- 'excerpt' => __( 'Excerpt View' )
153
  );
154
  }
155
  }
156
 
157
  /**
158
- * Make private properties readable for backwards compatibility.
159
  *
160
  * @since 4.0.0
161
- * @access public
162
  *
163
  * @param string $name Property to get.
164
  * @return mixed Property.
165
  */
166
  public function __get( $name ) {
167
- if ( in_array( $name, $this->compat_fields ) ) {
168
  return $this->$name;
169
  }
170
  }
171
 
172
  /**
173
- * Make private properties settable for backwards compatibility.
174
  *
175
  * @since 4.0.0
176
- * @access public
177
  *
178
  * @param string $name Property to check if set.
179
  * @param mixed $value Property value.
180
  * @return mixed Newly-set property.
181
  */
182
  public function __set( $name, $value ) {
183
- if ( in_array( $name, $this->compat_fields ) ) {
184
  return $this->$name = $value;
185
  }
186
  }
187
 
188
  /**
189
- * Make private properties checkable for backwards compatibility.
190
  *
191
  * @since 4.0.0
192
- * @access public
193
  *
194
  * @param string $name Property to check if set.
195
  * @return bool Whether the property is set.
196
  */
197
  public function __isset( $name ) {
198
- if ( in_array( $name, $this->compat_fields ) ) {
199
  return isset( $this->$name );
200
  }
201
  }
202
 
203
  /**
204
- * Make private properties un-settable for backwards compatibility.
205
  *
206
  * @since 4.0.0
207
- * @access public
208
  *
209
  * @param string $name Property to unset.
210
  */
211
  public function __unset( $name ) {
212
- if ( in_array( $name, $this->compat_fields ) ) {
213
  unset( $this->$name );
214
  }
215
  }
216
 
217
  /**
218
- * Make private/protected methods readable for backwards compatibility.
219
  *
220
  * @since 4.0.0
221
- * @access public
222
  *
223
- * @param callable $name Method to call.
224
- * @param array $arguments Arguments to pass when calling.
225
  * @return mixed|bool Return value of the callback, false otherwise.
226
  */
227
  public function __call( $name, $arguments ) {
228
- if ( in_array( $name, $this->compat_methods ) ) {
229
- return call_user_func_array( array( $this, $name ), $arguments );
230
  }
231
  return false;
232
  }
@@ -235,42 +244,44 @@ class Visual_Form_Builder_List_Table {
235
  * Checks the current user's permissions
236
  *
237
  * @since 3.1.0
238
- * @access public
239
  * @abstract
240
  */
241
  public function ajax_user_can() {
242
- die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
243
  }
244
 
245
  /**
246
  * Prepares the list of items for displaying.
 
247
  * @uses WP_List_Table::set_pagination_args()
248
  *
249
  * @since 3.1.0
250
- * @access public
251
  * @abstract
252
  */
253
  public function prepare_items() {
254
- die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
255
  }
256
 
257
  /**
258
  * An internal method that sets all the necessary pagination arguments
259
  *
260
- * @param array $args An associative array with information about the pagination
261
- * @access protected
262
  *
263
  * @param array|string $args Array or string of arguments with information about the pagination.
264
  */
265
  protected function set_pagination_args( $args ) {
266
- $args = wp_parse_args( $args, array(
267
- 'total_items' => 0,
268
- 'total_pages' => 0,
269
- 'per_page' => 0,
270
- ) );
 
 
 
271
 
272
- if ( !$args['total_pages'] && $args['per_page'] > 0 )
273
  $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
 
274
 
275
  // Redirect if page number is invalid and headers are not already sent.
276
  if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
@@ -285,7 +296,6 @@ class Visual_Form_Builder_List_Table {
285
  * Access the pagination args.
286
  *
287
  * @since 3.1.0
288
- * @access public
289
  *
290
  * @param string $key Pagination argument to retrieve. Common values include 'total_items',
291
  * 'total_pages', 'per_page', or 'infinite_scroll'.
@@ -296,8 +306,8 @@ class Visual_Form_Builder_List_Table {
296
  return $this->get_pagenum();
297
  }
298
 
299
- if ( isset( $this->_pagination_args[$key] ) ) {
300
- return $this->_pagination_args[$key];
301
  }
302
  }
303
 
@@ -305,62 +315,65 @@ class Visual_Form_Builder_List_Table {
305
  * Whether the table has items to display or not
306
  *
307
  * @since 3.1.0
308
- * @access public
309
  *
310
  * @return bool
311
  */
312
  public function has_items() {
313
- return !empty( $this->items );
314
  }
315
 
316
  /**
317
  * Message to be displayed when there are no items
318
  *
319
  * @since 3.1.0
320
- * @access public
321
  */
322
  public function no_items() {
323
  _e( 'No items found.' );
324
  }
325
 
326
  /**
327
- * Display the search box.
328
  *
329
  * @since 3.1.0
330
- * @access public
331
  *
332
- * @param string $text The search button text
333
- * @param string $input_id The search input id
334
  */
335
  public function search_box( $text, $input_id ) {
336
- if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
337
  return;
 
338
 
339
  $input_id = $input_id . '-search-input';
340
 
341
- if ( ! empty( $_REQUEST['orderby'] ) )
342
  echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
343
- if ( ! empty( $_REQUEST['order'] ) )
 
344
  echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
345
- if ( ! empty( $_REQUEST['post_mime_type'] ) )
 
346
  echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
347
- if ( ! empty( $_REQUEST['detached'] ) )
 
348
  echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
349
- ?>
 
350
  <p class="search-box">
351
  <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
352
  <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
353
- <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
354
  </p>
355
- <?php
356
  }
357
 
358
  /**
359
- * Get an associative array ( id => link ) with the list
360
- * of views available on this table.
 
 
361
  *
362
  * @since 3.1.0
363
- * @access protected
364
  *
365
  * @return array
366
  */
@@ -369,27 +382,27 @@ class Visual_Form_Builder_List_Table {
369
  }
370
 
371
  /**
372
- * Display the list of views available on this table.
373
  *
374
  * @since 3.1.0
375
- * @access public
376
  */
377
  public function views() {
378
  $views = $this->get_views();
379
  /**
380
- * Filter the list of available list table views.
381
  *
382
  * The dynamic portion of the hook name, `$this->screen->id`, refers
383
- * to the ID of the current screen, usually a string.
384
  *
385
- * @since 3.5.0
386
  *
387
- * @param array $views An array of available list table views.
388
  */
389
  $views = apply_filters( "views_{$this->screen->id}", $views );
390
 
391
- if ( empty( $views ) )
392
  return;
 
393
 
394
  $this->screen->render_screen_reader_content( 'heading_views' );
395
 
@@ -398,15 +411,33 @@ class Visual_Form_Builder_List_Table {
398
  $views[ $class ] = "\t<li class='$class'>$view";
399
  }
400
  echo implode( " |</li>\n", $views ) . "</li>\n";
401
- echo "</ul>";
402
  }
403
 
404
  /**
405
- * Get an associative array ( option_name => option_title ) with the list
406
- * of bulk actions available on this table.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  *
408
  * @since 3.1.0
409
- * @access protected
410
  *
411
  * @return array
412
  */
@@ -415,10 +446,9 @@ class Visual_Form_Builder_List_Table {
415
  }
416
 
417
  /**
418
- * Display the bulk actions dropdown.
419
  *
420
  * @since 3.1.0
421
- * @access protected
422
  *
423
  * @param string $which The location of the bulk actions: 'top' or 'bottom'.
424
  * This is designated as optional for backward compatibility.
@@ -426,35 +456,48 @@ class Visual_Form_Builder_List_Table {
426
  protected function bulk_actions( $which = '' ) {
427
  if ( is_null( $this->_actions ) ) {
428
  $this->_actions = $this->get_bulk_actions();
 
429
  /**
430
- * Filters the list table Bulk Actions drop-down.
431
  *
432
  * The dynamic portion of the hook name, `$this->screen->id`, refers
433
- * to the ID of the current screen, usually a string.
434
- *
435
- * This filter can currently only be used to remove bulk actions.
436
  *
437
- * @since 3.5.0
 
438
  *
439
  * @param array $actions An array of the available bulk actions.
440
  */
441
- $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
 
442
  $two = '';
443
  } else {
444
  $two = '2';
445
  }
446
 
447
- if ( empty( $this->_actions ) )
448
  return;
 
449
 
450
  echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>';
451
  echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
452
- echo '<option value="-1">' . __( 'Bulk Actions' ) . "</option>\n";
 
 
 
 
453
 
454
- foreach ( $this->_actions as $name => $title ) {
455
- $class = 'edit' === $name ? ' class="hide-if-no-js"' : '';
456
 
457
- echo "\t" . '<option value="' . $name . '"' . $class . '>' . $title . "</option>\n";
 
 
 
 
 
 
 
458
  }
459
 
460
  echo "</select>\n";
@@ -464,49 +507,58 @@ class Visual_Form_Builder_List_Table {
464
  }
465
 
466
  /**
467
- * Get the current action selected from the bulk actions dropdown.
468
  *
469
  * @since 3.1.0
470
- * @access public
471
  *
472
- * @return string|false The action name or False if no action was selected
473
  */
474
  public function current_action() {
475
- if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) )
476
  return false;
 
477
 
478
- if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
479
  return $_REQUEST['action'];
480
-
481
- if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
482
- return $_REQUEST['action2'];
483
 
484
  return false;
485
  }
486
 
487
  /**
488
- * Generate row actions div
489
  *
490
  * @since 3.1.0
491
- * @access protected
492
  *
493
- * @param array $actions The list of actions
494
- * @param bool $always_visible Whether the actions should be always visible
495
- * @return string
496
  */
497
  protected function row_actions( $actions, $always_visible = false ) {
498
  $action_count = count( $actions );
499
- $i = 0;
500
 
501
- if ( !$action_count )
502
  return '';
 
 
 
 
 
 
 
503
 
504
  $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
 
 
 
505
  foreach ( $actions as $action => $link ) {
506
  ++$i;
507
- ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
 
 
508
  $out .= "<span class='$action'>$link$sep</span>";
509
  }
 
510
  $out .= '</div>';
511
 
512
  $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
@@ -515,15 +567,14 @@ class Visual_Form_Builder_List_Table {
515
  }
516
 
517
  /**
518
- * Display a monthly dropdown for filtering items
519
  *
520
  * @since 3.1.0
521
- * @access protected
522
  *
523
- * @global wpdb $wpdb
524
- * @global WP_Locale $wp_locale
525
  *
526
- * @param string $post_type
527
  */
528
  protected function months_dropdown( $post_type ) {
529
  global $wpdb, $wp_locale;
@@ -540,95 +591,118 @@ class Visual_Form_Builder_List_Table {
540
  return;
541
  }
542
 
543
- $extra_checks = "AND post_status != 'auto-draft'";
544
- if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
545
- $extra_checks .= " AND post_status != 'trash'";
546
- } elseif ( isset( $_GET['post_status'] ) ) {
547
- $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
548
- }
 
 
 
 
 
 
 
 
 
 
 
549
 
550
- $months = $wpdb->get_results( $wpdb->prepare( "
551
- SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
552
- FROM $wpdb->posts
553
- WHERE post_type = %s
554
- $extra_checks
555
- ORDER BY post_date DESC
556
- ", $post_type ) );
 
 
 
 
 
 
557
 
558
  /**
559
  * Filters the 'Months' drop-down results.
560
  *
561
  * @since 3.7.0
562
  *
563
- * @param object $months The months drop-down query results.
564
- * @param string $post_type The post type.
565
  */
566
  $months = apply_filters( 'months_dropdown_results', $months, $post_type );
567
 
568
  $month_count = count( $months );
569
 
570
- if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
571
  return;
 
572
 
573
  $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
574
- ?>
575
- <label for="filter-by-date" class="screen-reader-text"><?php _e( 'Filter by date' ); ?></label>
576
  <select name="m" id="filter-by-date">
577
  <option<?php selected( $m, 0 ); ?> value="0"><?php _e( 'All dates' ); ?></option>
578
- <?php
579
  foreach ( $months as $arc_row ) {
580
- if ( 0 == $arc_row->year )
581
  continue;
 
582
 
583
  $month = zeroise( $arc_row->month, 2 );
584
- $year = $arc_row->year;
585
 
586
- printf( "<option %s value='%s'>%s</option>\n",
 
587
  selected( $m, $year . $month, false ),
588
  esc_attr( $arc_row->year . $month ),
589
- /* translators: 1: month name, 2: 4-digit year */
590
  sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
591
  );
592
  }
593
- ?>
594
  </select>
595
- <?php
596
  }
597
 
598
  /**
599
- * Display a view switcher
600
  *
601
  * @since 3.1.0
602
- * @access protected
603
  *
604
  * @param string $current_mode
605
  */
606
  protected function view_switcher( $current_mode ) {
607
- ?>
608
  <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
609
  <div class="view-switch">
610
- <?php
611
- foreach ( $this->modes as $mode => $title ) {
612
- $classes = array( 'view-' . $mode );
613
- if ( $current_mode === $mode )
614
- $classes[] = 'current';
615
- printf(
616
- "<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
617
- esc_url( add_query_arg( 'mode', $mode ) ),
618
- implode( ' ', $classes ),
619
- $title
620
- );
621
  }
 
 
 
 
 
 
 
 
622
  ?>
623
  </div>
624
- <?php
625
  }
626
 
627
  /**
628
- * Display a comment count bubble
629
  *
630
  * @since 3.1.0
631
- * @access protected
632
  *
633
  * @param int $post_id The post ID.
634
  * @param int $pending_comments Number of pending comments.
@@ -637,39 +711,82 @@ class Visual_Form_Builder_List_Table {
637
  $approved_comments = get_comments_number();
638
 
639
  $approved_comments_number = number_format_i18n( $approved_comments );
640
- $pending_comments_number = number_format_i18n( $pending_comments );
 
 
 
 
 
 
641
 
642
- $approved_only_phrase = sprintf( _n( '%s comment', '%s comments', $approved_comments ), $approved_comments_number );
643
- $approved_phrase = sprintf( _n( '%s approved comment', '%s approved comments', $approved_comments ), $approved_comments_number );
644
- $pending_phrase = sprintf( _n( '%s pending comment', '%s pending comments', $pending_comments ), $pending_comments_number );
 
 
 
 
 
 
 
 
645
 
646
- // No comments at all.
647
  if ( ! $approved_comments && ! $pending_comments ) {
648
- printf( '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">%s</span>',
 
 
649
  __( 'No comments' )
650
  );
651
- // Approved comments have different display depending on some conditions.
 
 
 
 
 
 
652
  } elseif ( $approved_comments ) {
653
- printf( '<a href="%s" class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
654
- esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'approved' ), admin_url( 'edit-comments.php' ) ) ),
 
 
 
 
 
 
 
 
 
 
655
  $approved_comments_number,
656
  $pending_comments ? $approved_phrase : $approved_only_phrase
657
  );
658
  } else {
659
- printf( '<span class="post-com-count post-com-count-no-comments"><span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
 
 
660
  $approved_comments_number,
661
  $pending_comments ? __( 'No approved comments' ) : __( 'No comments' )
662
  );
663
  }
664
 
665
  if ( $pending_comments ) {
666
- printf( '<a href="%s" class="post-com-count post-com-count-pending"><span class="comment-count-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
667
- esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'moderated' ), admin_url( 'edit-comments.php' ) ) ),
 
 
 
 
 
 
 
 
 
668
  $pending_comments_number,
669
  $pending_phrase
670
  );
671
  } else {
672
- printf( '<span class="post-com-count post-com-count-pending post-com-count-no-pending"><span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
 
673
  $pending_comments_number,
674
  $approved_comments ? __( 'No pending comments' ) : __( 'No comments' )
675
  );
@@ -677,27 +794,26 @@ class Visual_Form_Builder_List_Table {
677
  }
678
 
679
  /**
680
- * Get the current page number
681
  *
682
  * @since 3.1.0
683
- * @access public
684
  *
685
  * @return int
686
  */
687
  public function get_pagenum() {
688
  $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
689
 
690
- if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
691
  $pagenum = $this->_pagination_args['total_pages'];
 
692
 
693
  return max( 1, $pagenum );
694
  }
695
 
696
  /**
697
- * Get number of items to display on a single page
698
  *
699
  * @since 3.1.0
700
- * @access protected
701
  *
702
  * @param string $option
703
  * @param int $default
@@ -705,17 +821,29 @@ class Visual_Form_Builder_List_Table {
705
  */
706
  protected function get_items_per_page( $option, $default = 20 ) {
707
  $per_page = (int) get_user_option( $option );
708
- if ( empty( $per_page ) || $per_page < 1 )
709
  $per_page = $default;
 
710
 
711
  /**
712
  * Filters the number of items to be displayed on each page of the list table.
713
  *
714
- * The dynamic hook name, $option, refers to the `per_page` option depending
715
- * on the type of list table in use. Possible values include: 'edit_comments_per_page',
716
- * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
717
- * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
718
- * 'edit_{$post_type}_per_page', etc.
 
 
 
 
 
 
 
 
 
 
 
719
  *
720
  * @since 2.9.0
721
  *
@@ -725,10 +853,9 @@ class Visual_Form_Builder_List_Table {
725
  }
726
 
727
  /**
728
- * Display the pagination.
729
  *
730
  * @since 3.1.0
731
- * @access protected
732
  *
733
  * @param string $which
734
  */
@@ -737,8 +864,8 @@ class Visual_Form_Builder_List_Table {
737
  return;
738
  }
739
 
740
- $total_items = $this->_pagination_args['total_items'];
741
- $total_pages = $this->_pagination_args['total_pages'];
742
  $infinite_scroll = false;
743
  if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
744
  $infinite_scroll = $this->_pagination_args['infinite_scroll'];
@@ -748,9 +875,13 @@ class Visual_Form_Builder_List_Table {
748
  $this->screen->render_screen_reader_content( 'heading_pagination' );
749
  }
750
 
751
- $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
 
 
 
 
752
 
753
- $current = $this->get_pagenum();
754
  $removable_query_args = wp_removable_query_args();
755
 
756
  $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
@@ -762,27 +893,31 @@ class Visual_Form_Builder_List_Table {
762
  $total_pages_before = '<span class="paging-input">';
763
  $total_pages_after = '</span></span>';
764
 
765
- $disable_first = $disable_last = $disable_prev = $disable_next = false;
 
 
 
766
 
767
- if ( $current == 1 ) {
768
  $disable_first = true;
769
- $disable_prev = true;
770
- }
771
- if ( $current == 2 ) {
772
  $disable_first = true;
773
  }
774
- if ( $current == $total_pages ) {
775
  $disable_last = true;
776
  $disable_next = true;
777
- }
778
- if ( $current == $total_pages - 1 ) {
779
  $disable_last = true;
780
  }
781
 
782
  if ( $disable_first ) {
783
- $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&laquo;</span>';
784
  } else {
785
- $page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
 
786
  esc_url( remove_query_arg( 'paged', $current_url ) ),
787
  __( 'First page' ),
788
  '&laquo;'
@@ -790,10 +925,11 @@ class Visual_Form_Builder_List_Table {
790
  }
791
 
792
  if ( $disable_prev ) {
793
- $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>';
794
  } else {
795
- $page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
796
- esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
 
797
  __( 'Previous page' ),
798
  '&lsaquo;'
799
  );
@@ -803,29 +939,37 @@ class Visual_Form_Builder_List_Table {
803
  $html_current_page = $current;
804
  $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
805
  } else {
806
- $html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
 
807
  '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
808
  $current,
809
  strlen( $total_pages )
810
  );
811
  }
812
  $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
813
- $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
 
 
 
 
 
814
 
815
  if ( $disable_next ) {
816
- $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>';
817
  } else {
818
- $page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
819
- esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
 
820
  __( 'Next page' ),
821
  '&rsaquo;'
822
  );
823
  }
824
 
825
  if ( $disable_last ) {
826
- $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&raquo;</span>';
827
  } else {
828
- $page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
 
829
  esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
830
  __( 'Last page' ),
831
  '&raquo;'
@@ -836,7 +980,7 @@ class Visual_Form_Builder_List_Table {
836
  if ( ! empty( $infinite_scroll ) ) {
837
  $pagination_links_class .= ' hide-if-js';
838
  }
839
- $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
840
 
841
  if ( $total_pages ) {
842
  $page_class = $total_pages < 2 ? ' one-page' : '';
@@ -849,29 +993,29 @@ class Visual_Form_Builder_List_Table {
849
  }
850
 
851
  /**
852
- * Get a list of columns. The format is:
853
- * 'internal-name' => 'Title'
 
 
854
  *
855
  * @since 3.1.0
856
- * @access public
857
  * @abstract
858
  *
859
  * @return array
860
  */
861
  public function get_columns() {
862
- die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
863
  }
864
 
865
  /**
866
- * Get a list of sortable columns. The format is:
867
- * 'internal-name' => 'orderby'
868
- * or
869
- * 'internal-name' => array( 'orderby', true )
870
  *
871
- * The second format will make the initial sorting order be descending
 
 
 
872
  *
873
  * @since 3.1.0
874
- * @access protected
875
  *
876
  * @return array
877
  */
@@ -883,13 +1027,12 @@ class Visual_Form_Builder_List_Table {
883
  * Gets the name of the default primary column.
884
  *
885
  * @since 4.3.0
886
- * @access protected
887
  *
888
  * @return string Name of the default primary column, in this case, an empty string.
889
  */
890
  protected function get_default_primary_column_name() {
891
  $columns = $this->get_columns();
892
- $column = '';
893
 
894
  if ( empty( $columns ) ) {
895
  return $column;
@@ -910,7 +1053,7 @@ class Visual_Form_Builder_List_Table {
910
  }
911
 
912
  /**
913
- * Public wrapper for Visual_Form_Builder_List_Table::get_default_primary_column_name().
914
  *
915
  * @since 4.4.0
916
  *
@@ -924,7 +1067,6 @@ class Visual_Form_Builder_List_Table {
924
  * Gets the name of the primary column.
925
  *
926
  * @since 4.3.0
927
- * @access protected
928
  *
929
  * @return string The name of the primary column.
930
  */
@@ -932,10 +1074,10 @@ class Visual_Form_Builder_List_Table {
932
  $columns = get_column_headers( $this->screen );
933
  $default = $this->get_default_primary_column_name();
934
 
935
- // If the primary column doesn't exist fall back to the
936
- // first non-checkbox column.
937
  if ( ! isset( $columns[ $default ] ) ) {
938
- $default = Visual_Form_Builder_List_Table::get_default_primary_column_name();
939
  }
940
 
941
  /**
@@ -946,7 +1088,7 @@ class Visual_Form_Builder_List_Table {
946
  * @param string $default Column name default for the specific list table, e.g. 'name'.
947
  * @param string $context Screen ID for specific list table, e.g. 'plugins'.
948
  */
949
- $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
950
 
951
  if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
952
  $column = $default;
@@ -956,18 +1098,22 @@ class Visual_Form_Builder_List_Table {
956
  }
957
 
958
  /**
959
- * Get a list of all, hidden and sortable columns, with filter applied
960
  *
961
  * @since 3.1.0
962
- * @access protected
963
  *
964
  * @return array
965
  */
966
  protected function get_column_info() {
967
- // $_column_headers is already set / cached
968
  if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
969
- // Back-compat for list tables that have been manually setting $_column_headers for horse reasons.
970
- // In 4.3, we added a fourth argument for primary column.
 
 
 
 
 
971
  $column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
972
  foreach ( $this->_column_headers as $key => $value ) {
973
  $column_headers[ $key ] = $value;
@@ -977,16 +1123,16 @@ class Visual_Form_Builder_List_Table {
977
  }
978
 
979
  $columns = get_column_headers( $this->screen );
980
- $hidden = get_hidden_columns( $this->screen );
981
 
982
  $sortable_columns = $this->get_sortable_columns();
983
  /**
984
  * Filters the list table sortable columns for a specific screen.
985
  *
986
  * The dynamic portion of the hook name, `$this->screen->id`, refers
987
- * to the ID of the current screen, usually a string.
988
  *
989
- * @since 3.5.0
990
  *
991
  * @param array $sortable_columns An array of sortable columns.
992
  */
@@ -994,45 +1140,43 @@ class Visual_Form_Builder_List_Table {
994
 
995
  $sortable = array();
996
  foreach ( $_sortable as $id => $data ) {
997
- if ( empty( $data ) )
998
  continue;
 
999
 
1000
  $data = (array) $data;
1001
- if ( !isset( $data[1] ) )
1002
  $data[1] = false;
 
1003
 
1004
- $sortable[$id] = $data;
1005
  }
1006
 
1007
- $primary = $this->get_primary_column_name();
1008
  $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
1009
 
1010
  return $this->_column_headers;
1011
  }
1012
 
1013
  /**
1014
- * Return number of visible columns
1015
  *
1016
  * @since 3.1.0
1017
- * @access public
1018
  *
1019
  * @return int
1020
  */
1021
  public function get_column_count() {
1022
  list ( $columns, $hidden ) = $this->get_column_info();
1023
- $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
1024
  return count( $columns ) - count( $hidden );
1025
  }
1026
 
1027
  /**
1028
- * Print column headers, accounting for hidden and sortable columns.
1029
  *
1030
  * @since 3.1.0
1031
- * @access public
1032
- *
1033
- * @staticvar int $cb_counter
1034
  *
1035
- * @param bool $with_id Whether to set the id attribute or not
1036
  */
1037
  public function print_column_headers( $with_id = true ) {
1038
  list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
@@ -1054,7 +1198,7 @@ class Visual_Form_Builder_List_Table {
1054
 
1055
  if ( ! empty( $columns['cb'] ) ) {
1056
  static $cb_counter = 1;
1057
- $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
1058
  . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
1059
  $cb_counter++;
1060
  }
@@ -1062,51 +1206,62 @@ class Visual_Form_Builder_List_Table {
1062
  foreach ( $columns as $column_key => $column_display_name ) {
1063
  $class = array( 'manage-column', "column-$column_key" );
1064
 
1065
- if ( in_array( $column_key, $hidden ) ) {
1066
  $class[] = 'hidden';
1067
  }
1068
 
1069
- if ( 'cb' === $column_key )
1070
  $class[] = 'check-column';
1071
- elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
1072
  $class[] = 'num';
 
1073
 
1074
  if ( $column_key === $primary ) {
1075
  $class[] = 'column-primary';
1076
  }
1077
 
1078
- if ( isset( $sortable[$column_key] ) ) {
1079
- list( $orderby, $desc_first ) = $sortable[$column_key];
1080
 
1081
  if ( $current_orderby === $orderby ) {
1082
  $order = 'asc' === $current_order ? 'desc' : 'asc';
 
1083
  $class[] = 'sorted';
1084
  $class[] = $current_order;
1085
  } else {
1086
- $order = $desc_first ? 'desc' : 'asc';
 
 
 
 
 
1087
  $class[] = 'sortable';
1088
- $class[] = $desc_first ? 'asc' : 'desc';
1089
  }
1090
 
1091
- $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
 
 
 
 
1092
  }
1093
 
1094
- $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
1095
  $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
1096
- $id = $with_id ? "id='$column_key'" : '';
1097
 
1098
- if ( !empty( $class ) )
1099
- $class = "class='" . join( ' ', $class ) . "'";
 
1100
 
1101
  echo "<$tag $scope $id $class>$column_display_name</$tag>";
1102
  }
1103
  }
1104
 
1105
  /**
1106
- * Display the table
1107
  *
1108
  * @since 3.1.0
1109
- * @access public
1110
  */
1111
  public function display() {
1112
  $singular = $this->_args['singular'];
@@ -1114,7 +1269,7 @@ class Visual_Form_Builder_List_Table {
1114
  $this->display_tablenav( 'top' );
1115
 
1116
  $this->screen->render_screen_reader_content( 'heading_list' );
1117
- ?>
1118
  <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
1119
  <thead>
1120
  <tr>
@@ -1122,10 +1277,13 @@ class Visual_Form_Builder_List_Table {
1122
  </tr>
1123
  </thead>
1124
 
1125
- <tbody id="the-list"<?php
 
1126
  if ( $singular ) {
1127
  echo " data-wp-lists='list:$singular'";
1128
- } ?>>
 
 
1129
  <?php $this->display_rows_or_placeholder(); ?>
1130
  </tbody>
1131
 
@@ -1136,27 +1294,29 @@ class Visual_Form_Builder_List_Table {
1136
  </tfoot>
1137
 
1138
  </table>
1139
- <?php
1140
  $this->display_tablenav( 'bottom' );
1141
  }
1142
 
1143
  /**
1144
- * Get a list of CSS classes for the list table table tag.
1145
  *
1146
  * @since 3.1.0
1147
- * @access protected
1148
  *
1149
- * @return array List of CSS classes for the table tag.
1150
  */
1151
  protected function get_table_classes() {
1152
- return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
 
 
 
 
1153
  }
1154
 
1155
  /**
1156
- * Generate the table navigation above or below the table
1157
  *
1158
  * @since 3.1.0
1159
- * @access protected
1160
  * @param string $which
1161
  */
1162
  protected function display_tablenav( $which ) {
@@ -1166,35 +1326,34 @@ class Visual_Form_Builder_List_Table {
1166
  ?>
1167
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
1168
 
1169
- <?php if ( $this->has_items() ): ?>
1170
  <div class="alignleft actions bulkactions">
1171
  <?php $this->bulk_actions( $which ); ?>
1172
  </div>
1173
- <?php endif;
 
1174
  $this->extra_tablenav( $which );
1175
  $this->pagination( $which );
1176
- ?>
1177
 
1178
  <br class="clear" />
1179
  </div>
1180
- <?php
1181
  }
1182
 
1183
  /**
1184
- * Extra controls to be displayed between bulk actions and pagination
1185
  *
1186
  * @since 3.1.0
1187
- * @access protected
1188
  *
1189
  * @param string $which
1190
  */
1191
  protected function extra_tablenav( $which ) {}
1192
 
1193
  /**
1194
- * Generate the tbody element for the list table.
1195
  *
1196
  * @since 3.1.0
1197
- * @access public
1198
  */
1199
  public function display_rows_or_placeholder() {
1200
  if ( $this->has_items() ) {
@@ -1207,23 +1366,22 @@ class Visual_Form_Builder_List_Table {
1207
  }
1208
 
1209
  /**
1210
- * Generate the table rows
1211
  *
1212
  * @since 3.1.0
1213
- * @access public
1214
  */
1215
  public function display_rows() {
1216
- foreach ( $this->items as $item )
1217
  $this->single_row( $item );
 
1218
  }
1219
 
1220
  /**
1221
- * Generates content for a single row of the table
1222
  *
1223
  * @since 3.1.0
1224
- * @access public
1225
  *
1226
- * @param object $item The current item
1227
  */
1228
  public function single_row( $item ) {
1229
  echo '<tr>';
@@ -1232,25 +1390,22 @@ class Visual_Form_Builder_List_Table {
1232
  }
1233
 
1234
  /**
1235
- *
1236
- * @param object $item
1237
  * @param string $column_name
1238
  */
1239
  protected function column_default( $item, $column_name ) {}
1240
 
1241
  /**
1242
- *
1243
- * @param object $item
1244
  */
1245
  protected function column_cb( $item ) {}
1246
 
1247
  /**
1248
- * Generates the columns for a single row of the table
1249
  *
1250
  * @since 3.1.0
1251
- * @access protected
1252
  *
1253
- * @param object $item The current item
1254
  */
1255
  protected function single_row_columns( $item ) {
1256
  list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
@@ -1261,13 +1416,13 @@ class Visual_Form_Builder_List_Table {
1261
  $classes .= ' has-row-actions column-primary';
1262
  }
1263
 
1264
- if ( in_array( $column_name, $hidden ) ) {
1265
  $classes .= ' hidden';
1266
  }
1267
 
1268
  // Comments column uses HTML in the display name with screen reader text.
1269
- // Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
1270
- $data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
1271
 
1272
  $attributes = "class='$classes' $data";
1273
 
@@ -1287,12 +1442,12 @@ class Visual_Form_Builder_List_Table {
1287
  echo "<td $attributes>";
1288
  echo call_user_func( array( $this, 'column_' . $column_name ), $item );
1289
  echo $this->handle_row_actions( $item, $column_name, $primary );
1290
- echo "</td>";
1291
  } else {
1292
  echo "<td $attributes>";
1293
  echo $this->column_default( $item, $column_name );
1294
  echo $this->handle_row_actions( $item, $column_name, $primary );
1295
- echo "</td>";
1296
  }
1297
  }
1298
  }
@@ -1301,22 +1456,21 @@ class Visual_Form_Builder_List_Table {
1301
  * Generates and display row actions links for the list table.
1302
  *
1303
  * @since 4.3.0
1304
- * @access protected
1305
  *
1306
- * @param object $item The item being acted upon.
1307
- * @param string $column_name Current column name.
1308
- * @param string $primary Primary column name.
1309
- * @return string The row actions HTML, or an empty string if the current column is the primary column.
 
1310
  */
1311
  protected function handle_row_actions( $item, $column_name, $primary ) {
1312
  return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
1313
- }
1314
 
1315
  /**
1316
- * Handle an incoming ajax request (called from admin-ajax.php)
1317
  *
1318
  * @since 3.1.0
1319
- * @access public
1320
  */
1321
  public function ajax_response() {
1322
  $this->prepare_items();
@@ -1334,12 +1488,13 @@ class Visual_Form_Builder_List_Table {
1334
 
1335
  if ( isset( $this->_pagination_args['total_items'] ) ) {
1336
  $response['total_items_i18n'] = sprintf(
 
1337
  _n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1338
  number_format_i18n( $this->_pagination_args['total_items'] )
1339
  );
1340
  }
1341
  if ( isset( $this->_pagination_args['total_pages'] ) ) {
1342
- $response['total_pages'] = $this->_pagination_args['total_pages'];
1343
  $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1344
  }
1345
 
@@ -1347,9 +1502,9 @@ class Visual_Form_Builder_List_Table {
1347
  }
1348
 
1349
  /**
1350
- * Send required variables to JavaScript land
1351
  *
1352
- * @access public
1353
  */
1354
  public function _js_vars() {
1355
  $args = array(
@@ -1357,7 +1512,7 @@ class Visual_Form_Builder_List_Table {
1357
  'screen' => array(
1358
  'id' => $this->screen->id,
1359
  'base' => $this->screen->base,
1360
- )
1361
  );
1362
 
1363
  printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
16
  *
17
  * @since 3.1.0
18
  * @var array
 
19
  */
20
  public $items;
21
 
24
  *
25
  * @since 3.1.0
26
  * @var array
 
27
  */
28
  protected $_args;
29
 
39
  * The current screen.
40
  *
41
  * @since 3.1.0
42
+ * @var WP_Screen
 
43
  */
44
  protected $screen;
45
 
48
  *
49
  * @since 3.1.0
50
  * @var array
 
51
  */
52
  private $_actions;
53
 
56
  *
57
  * @since 3.1.0
58
  * @var string
 
59
  */
60
  private $_pagination;
61
 
64
  *
65
  * @since 4.1.0
66
  * @var array
 
67
  */
68
  protected $modes = array();
69
 
70
  /**
71
+ * Stores the value returned by ->get_column_info().
72
  *
73
+ * @since 4.1.0
74
  * @var array
75
  */
76
  protected $_column_headers;
77
 
78
  /**
79
+ * {@internal Missing Summary}
80
+ *
81
  * @var array
82
  */
83
  protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
84
 
85
  /**
86
+ * {@internal Missing Summary}
87
+ *
88
  * @var array
89
  */
90
+ protected $compat_methods = array(
91
+ 'set_pagination_args',
92
+ 'get_views',
93
+ 'get_bulk_actions',
94
+ 'bulk_actions',
95
+ 'row_actions',
96
+ 'months_dropdown',
97
+ 'view_switcher',
98
+ 'comments_bubble',
99
+ 'get_items_per_page',
100
+ 'pagination',
101
+ 'get_sortable_columns',
102
+ 'get_column_info',
103
+ 'get_table_classes',
104
+ 'display_tablenav',
105
+ 'extra_tablenav',
106
+ 'single_row_columns',
107
+ );
108
 
109
  /**
110
  * Constructor.
113
  * the default $args.
114
  *
115
  * @since 3.1.0
 
116
  *
117
  * @param array|string $args {
118
  * Array or string of arguments.
122
  * in the list table, e.g. 'posts'. Default empty.
123
  * @type string $singular Singular label for an object being listed, e.g. 'post'.
124
  * Default empty
125
+ * @type bool $ajax Whether the list table supports Ajax. This includes loading
126
  * and sorting data, for example. If true, the class will call
127
+ * the _js_vars() method in the footer to provide variables
128
+ * to any scripts handling Ajax events. Default false.
129
  * @type string $screen String containing the hook name used to determine the current
130
  * screen. If left null, the current screen will be automatically set.
131
  * Default null.
132
  * }
133
  */
134
  public function __construct( $args = array() ) {
135
+ $args = wp_parse_args(
136
+ $args,
137
+ array(
138
+ 'plural' => '',
139
+ 'singular' => '',
140
+ 'ajax' => false,
141
+ 'screen' => null,
142
+ )
143
+ );
144
 
145
  $this->screen = convert_to_screen( $args['screen'] );
146
 
147
  add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
148
 
149
+ if ( ! $args['plural'] ) {
150
  $args['plural'] = $this->screen->base;
151
+ }
152
 
153
+ $args['plural'] = sanitize_key( $args['plural'] );
154
  $args['singular'] = sanitize_key( $args['singular'] );
155
 
156
  $this->_args = $args;
162
 
163
  if ( empty( $this->modes ) ) {
164
  $this->modes = array(
165
+ 'list' => __( 'Compact view' ),
166
+ 'excerpt' => __( 'Extended view' ),
167
  );
168
  }
169
  }
170
 
171
  /**
172
+ * Make private properties readable for backward compatibility.
173
  *
174
  * @since 4.0.0
 
175
  *
176
  * @param string $name Property to get.
177
  * @return mixed Property.
178
  */
179
  public function __get( $name ) {
180
+ if ( in_array( $name, $this->compat_fields, true ) ) {
181
  return $this->$name;
182
  }
183
  }
184
 
185
  /**
186
+ * Make private properties settable for backward compatibility.
187
  *
188
  * @since 4.0.0
 
189
  *
190
  * @param string $name Property to check if set.
191
  * @param mixed $value Property value.
192
  * @return mixed Newly-set property.
193
  */
194
  public function __set( $name, $value ) {
195
+ if ( in_array( $name, $this->compat_fields, true ) ) {
196
  return $this->$name = $value;
197
  }
198
  }
199
 
200
  /**
201
+ * Make private properties checkable for backward compatibility.
202
  *
203
  * @since 4.0.0
 
204
  *
205
  * @param string $name Property to check if set.
206
  * @return bool Whether the property is set.
207
  */
208
  public function __isset( $name ) {
209
+ if ( in_array( $name, $this->compat_fields, true ) ) {
210
  return isset( $this->$name );
211
  }
212
  }
213
 
214
  /**
215
+ * Make private properties un-settable for backward compatibility.
216
  *
217
  * @since 4.0.0
 
218
  *
219
  * @param string $name Property to unset.
220
  */
221
  public function __unset( $name ) {
222
+ if ( in_array( $name, $this->compat_fields, true ) ) {
223
  unset( $this->$name );
224
  }
225
  }
226
 
227
  /**
228
+ * Make private/protected methods readable for backward compatibility.
229
  *
230
  * @since 4.0.0
 
231
  *
232
+ * @param string $name Method to call.
233
+ * @param array $arguments Arguments to pass when calling.
234
  * @return mixed|bool Return value of the callback, false otherwise.
235
  */
236
  public function __call( $name, $arguments ) {
237
+ if ( in_array( $name, $this->compat_methods, true ) ) {
238
+ return $this->$name( ...$arguments );
239
  }
240
  return false;
241
  }
244
  * Checks the current user's permissions
245
  *
246
  * @since 3.1.0
 
247
  * @abstract
248
  */
249
  public function ajax_user_can() {
250
+ die( 'function WP_List_Table::ajax_user_can() must be overridden in a subclass.' );
251
  }
252
 
253
  /**
254
  * Prepares the list of items for displaying.
255
+ *
256
  * @uses WP_List_Table::set_pagination_args()
257
  *
258
  * @since 3.1.0
 
259
  * @abstract
260
  */
261
  public function prepare_items() {
262
+ die( 'function WP_List_Table::prepare_items() must be overridden in a subclass.' );
263
  }
264
 
265
  /**
266
  * An internal method that sets all the necessary pagination arguments
267
  *
268
+ * @since 3.1.0
 
269
  *
270
  * @param array|string $args Array or string of arguments with information about the pagination.
271
  */
272
  protected function set_pagination_args( $args ) {
273
+ $args = wp_parse_args(
274
+ $args,
275
+ array(
276
+ 'total_items' => 0,
277
+ 'total_pages' => 0,
278
+ 'per_page' => 0,
279
+ )
280
+ );
281
 
282
+ if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
283
  $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
284
+ }
285
 
286
  // Redirect if page number is invalid and headers are not already sent.
287
  if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
296
  * Access the pagination args.
297
  *
298
  * @since 3.1.0
 
299
  *
300
  * @param string $key Pagination argument to retrieve. Common values include 'total_items',
301
  * 'total_pages', 'per_page', or 'infinite_scroll'.
306
  return $this->get_pagenum();
307
  }
308
 
309
+ if ( isset( $this->_pagination_args[ $key ] ) ) {
310
+ return $this->_pagination_args[ $key ];
311
  }
312
  }
313
 
315
  * Whether the table has items to display or not
316
  *
317
  * @since 3.1.0
 
318
  *
319
  * @return bool
320
  */
321
  public function has_items() {
322
+ return ! empty( $this->items );
323
  }
324
 
325
  /**
326
  * Message to be displayed when there are no items
327
  *
328
  * @since 3.1.0
 
329
  */
330
  public function no_items() {
331
  _e( 'No items found.' );
332
  }
333
 
334
  /**
335
+ * Displays the search box.
336
  *
337
  * @since 3.1.0
 
338
  *
339
+ * @param string $text The 'submit' button label.
340
+ * @param string $input_id ID attribute value for the search input field.
341
  */
342
  public function search_box( $text, $input_id ) {
343
+ if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
344
  return;
345
+ }
346
 
347
  $input_id = $input_id . '-search-input';
348
 
349
+ if ( ! empty( $_REQUEST['orderby'] ) ) {
350
  echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
351
+ }
352
+ if ( ! empty( $_REQUEST['order'] ) ) {
353
  echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
354
+ }
355
+ if ( ! empty( $_REQUEST['post_mime_type'] ) ) {
356
  echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
357
+ }
358
+ if ( ! empty( $_REQUEST['detached'] ) ) {
359
  echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
360
+ }
361
+ ?>
362
  <p class="search-box">
363
  <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
364
  <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
365
+ <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
366
  </p>
367
+ <?php
368
  }
369
 
370
  /**
371
+ * Gets the list of views available on this table.
372
+ *
373
+ * The format is an associative array:
374
+ * - `'id' => 'link'`
375
  *
376
  * @since 3.1.0
 
377
  *
378
  * @return array
379
  */
382
  }
383
 
384
  /**
385
+ * Displays the list of views available on this table.
386
  *
387
  * @since 3.1.0
 
388
  */
389
  public function views() {
390
  $views = $this->get_views();
391
  /**
392
+ * Filters the list of available list table views.
393
  *
394
  * The dynamic portion of the hook name, `$this->screen->id`, refers
395
+ * to the ID of the current screen.
396
  *
397
+ * @since 3.1.0
398
  *
399
+ * @param string[] $views An array of available list table views.
400
  */
401
  $views = apply_filters( "views_{$this->screen->id}", $views );
402
 
403
+ if ( empty( $views ) ) {
404
  return;
405
+ }
406
 
407
  $this->screen->render_screen_reader_content( 'heading_views' );
408
 
411
  $views[ $class ] = "\t<li class='$class'>$view";
412
  }
413
  echo implode( " |</li>\n", $views ) . "</li>\n";
414
+ echo '</ul>';
415
  }
416
 
417
  /**
418
+ * Retrieves the list of bulk actions available for this table.
419
+ *
420
+ * The format is an associative array where each element represents either a top level option value and label, or
421
+ * an array representing an optgroup and its options.
422
+ *
423
+ * For a standard option, the array element key is the field value and the array element value is the field label.
424
+ *
425
+ * For an optgroup, the array element key is the label and the array element value is an associative array of
426
+ * options as above.
427
+ *
428
+ * Example:
429
+ *
430
+ * [
431
+ * 'edit' => 'Edit',
432
+ * 'delete' => 'Delete',
433
+ * 'Change State' => [
434
+ * 'feature' => 'Featured',
435
+ * 'sale' => 'On Sale',
436
+ * ]
437
+ * ]
438
  *
439
  * @since 3.1.0
440
+ * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup.
441
  *
442
  * @return array
443
  */
446
  }
447
 
448
  /**
449
+ * Displays the bulk actions dropdown.
450
  *
451
  * @since 3.1.0
 
452
  *
453
  * @param string $which The location of the bulk actions: 'top' or 'bottom'.
454
  * This is designated as optional for backward compatibility.
456
  protected function bulk_actions( $which = '' ) {
457
  if ( is_null( $this->_actions ) ) {
458
  $this->_actions = $this->get_bulk_actions();
459
+
460
  /**
461
+ * Filters the items in the bulk actions menu of the list table.
462
  *
463
  * The dynamic portion of the hook name, `$this->screen->id`, refers
464
+ * to the ID of the current screen.
 
 
465
  *
466
+ * @since 3.1.0
467
+ * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup.
468
  *
469
  * @param array $actions An array of the available bulk actions.
470
  */
471
+ $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
472
+
473
  $two = '';
474
  } else {
475
  $two = '2';
476
  }
477
 
478
+ if ( empty( $this->_actions ) ) {
479
  return;
480
+ }
481
 
482
  echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>';
483
  echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
484
+ echo '<option value="-1">' . __( 'Bulk actions' ) . "</option>\n";
485
+
486
+ foreach ( $this->_actions as $key => $value ) {
487
+ if ( is_array( $value ) ) {
488
+ echo "\t" . '<optgroup label="' . esc_attr( $key ) . '">' . "\n";
489
 
490
+ foreach ( $value as $name => $title ) {
491
+ $class = ( 'edit' === $name ) ? ' class="hide-if-no-js"' : '';
492
 
493
+ echo "\t\t" . '<option value="' . esc_attr( $name ) . '"' . $class . '>' . $title . "</option>\n";
494
+ }
495
+ echo "\t" . "</optgroup>\n";
496
+ } else {
497
+ $class = ( 'edit' === $key ) ? ' class="hide-if-no-js"' : '';
498
+
499
+ echo "\t" . '<option value="' . esc_attr( $key ) . '"' . $class . '>' . $value . "</option>\n";
500
+ }
501
  }
502
 
503
  echo "</select>\n";
507
  }
508
 
509
  /**
510
+ * Gets the current action selected from the bulk actions dropdown.
511
  *
512
  * @since 3.1.0
 
513
  *
514
+ * @return string|false The action name. False if no action was selected.
515
  */
516
  public function current_action() {
517
+ if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) ) {
518
  return false;
519
+ }
520
 
521
+ if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] ) {
522
  return $_REQUEST['action'];
523
+ }
 
 
524
 
525
  return false;
526
  }
527
 
528
  /**
529
+ * Generates the required HTML for a list of row action links.
530
  *
531
  * @since 3.1.0
 
532
  *
533
+ * @param string[] $actions An array of action links.
534
+ * @param bool $always_visible Whether the actions should be always visible.
535
+ * @return string The HTML for the row actions.
536
  */
537
  protected function row_actions( $actions, $always_visible = false ) {
538
  $action_count = count( $actions );
 
539
 
540
+ if ( ! $action_count ) {
541
  return '';
542
+ }
543
+
544
+ $mode = get_user_setting( 'posts_list_mode', 'list' );
545
+
546
+ if ( 'excerpt' === $mode ) {
547
+ $always_visible = true;
548
+ }
549
 
550
  $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
551
+
552
+ $i = 0;
553
+
554
  foreach ( $actions as $action => $link ) {
555
  ++$i;
556
+
557
+ $sep = ( $i < $action_count ) ? ' | ' : '';
558
+
559
  $out .= "<span class='$action'>$link$sep</span>";
560
  }
561
+
562
  $out .= '</div>';
563
 
564
  $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
567
  }
568
 
569
  /**
570
+ * Displays a dropdown for filtering items in the list table by month.
571
  *
572
  * @since 3.1.0
 
573
  *
574
+ * @global wpdb $wpdb WordPress database abstraction object.
575
+ * @global WP_Locale $wp_locale WordPress date and time locale object.
576
  *
577
+ * @param string $post_type The post type.
578
  */
579
  protected function months_dropdown( $post_type ) {
580
  global $wpdb, $wp_locale;
591
  return;
592
  }
593
 
594
+ /**
595
+ * Filters to short-circuit performing the months dropdown query.
596
+ *
597
+ * @since 5.7.0
598
+ *
599
+ * @param object[]|false $months 'Months' drop-down results. Default false.
600
+ * @param string $post_type The post type.
601
+ */
602
+ $months = apply_filters( 'pre_months_dropdown_query', false, $post_type );
603
+
604
+ if ( ! is_array( $months ) ) {
605
+ $extra_checks = "AND post_status != 'auto-draft'";
606
+ if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
607
+ $extra_checks .= " AND post_status != 'trash'";
608
+ } elseif ( isset( $_GET['post_status'] ) ) {
609
+ $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
610
+ }
611
 
612
+ $months = $wpdb->get_results(
613
+ $wpdb->prepare(
614
+ "
615
+ SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
616
+ FROM $wpdb->posts
617
+ WHERE post_type = %s
618
+ $extra_checks
619
+ ORDER BY post_date DESC
620
+ ",
621
+ $post_type
622
+ )
623
+ );
624
+ }
625
 
626
  /**
627
  * Filters the 'Months' drop-down results.
628
  *
629
  * @since 3.7.0
630
  *
631
+ * @param object[] $months Array of the months drop-down query results.
632
+ * @param string $post_type The post type.
633
  */
634
  $months = apply_filters( 'months_dropdown_results', $months, $post_type );
635
 
636
  $month_count = count( $months );
637
 
638
+ if ( ! $month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) {
639
  return;
640
+ }
641
 
642
  $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
643
+ ?>
644
+ <label for="filter-by-date" class="screen-reader-text"><?php echo get_post_type_object( $post_type )->labels->filter_by_date; ?></label>
645
  <select name="m" id="filter-by-date">
646
  <option<?php selected( $m, 0 ); ?> value="0"><?php _e( 'All dates' ); ?></option>
647
+ <?php
648
  foreach ( $months as $arc_row ) {
649
+ if ( 0 == $arc_row->year ) {
650
  continue;
651
+ }
652
 
653
  $month = zeroise( $arc_row->month, 2 );
654
+ $year = $arc_row->year;
655
 
656
+ printf(
657
+ "<option %s value='%s'>%s</option>\n",
658
  selected( $m, $year . $month, false ),
659
  esc_attr( $arc_row->year . $month ),
660
+ /* translators: 1: Month name, 2: 4-digit year. */
661
  sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
662
  );
663
  }
664
+ ?>
665
  </select>
666
+ <?php
667
  }
668
 
669
  /**
670
+ * Displays a view switcher.
671
  *
672
  * @since 3.1.0
 
673
  *
674
  * @param string $current_mode
675
  */
676
  protected function view_switcher( $current_mode ) {
677
+ ?>
678
  <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
679
  <div class="view-switch">
680
+ <?php
681
+ foreach ( $this->modes as $mode => $title ) {
682
+ $classes = array( 'view-' . $mode );
683
+ $aria_current = '';
684
+
685
+ if ( $current_mode === $mode ) {
686
+ $classes[] = 'current';
687
+ $aria_current = ' aria-current="page"';
 
 
 
688
  }
689
+
690
+ printf(
691
+ "<a href='%s' class='%s' id='view-switch-$mode'$aria_current><span class='screen-reader-text'>%s</span></a>\n",
692
+ esc_url( remove_query_arg( 'attachment-filter', add_query_arg( 'mode', $mode ) ) ),
693
+ implode( ' ', $classes ),
694
+ $title
695
+ );
696
+ }
697
  ?>
698
  </div>
699
+ <?php
700
  }
701
 
702
  /**
703
+ * Displays a comment count bubble.
704
  *
705
  * @since 3.1.0
 
706
  *
707
  * @param int $post_id The post ID.
708
  * @param int $pending_comments Number of pending comments.
711
  $approved_comments = get_comments_number();
712
 
713
  $approved_comments_number = number_format_i18n( $approved_comments );
714
+ $pending_comments_number = number_format_i18n( $pending_comments );
715
+
716
+ $approved_only_phrase = sprintf(
717
+ /* translators: %s: Number of comments. */
718
+ _n( '%s comment', '%s comments', $approved_comments ),
719
+ $approved_comments_number
720
+ );
721
 
722
+ $approved_phrase = sprintf(
723
+ /* translators: %s: Number of comments. */
724
+ _n( '%s approved comment', '%s approved comments', $approved_comments ),
725
+ $approved_comments_number
726
+ );
727
+
728
+ $pending_phrase = sprintf(
729
+ /* translators: %s: Number of comments. */
730
+ _n( '%s pending comment', '%s pending comments', $pending_comments ),
731
+ $pending_comments_number
732
+ );
733
 
 
734
  if ( ! $approved_comments && ! $pending_comments ) {
735
+ // No comments at all.
736
+ printf(
737
+ '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">%s</span>',
738
  __( 'No comments' )
739
  );
740
+ } elseif ( $approved_comments && 'trash' === get_post_status( $post_id ) ) {
741
+ // Don't link the comment bubble for a trashed post.
742
+ printf(
743
+ '<span class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
744
+ $approved_comments_number,
745
+ $pending_comments ? $approved_phrase : $approved_only_phrase
746
+ );
747
  } elseif ( $approved_comments ) {
748
+ // Link the comment bubble to approved comments.
749
+ printf(
750
+ '<a href="%s" class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
751
+ esc_url(
752
+ add_query_arg(
753
+ array(
754
+ 'p' => $post_id,
755
+ 'comment_status' => 'approved',
756
+ ),
757
+ admin_url( 'edit-comments.php' )
758
+ )
759
+ ),
760
  $approved_comments_number,
761
  $pending_comments ? $approved_phrase : $approved_only_phrase
762
  );
763
  } else {
764
+ // Don't link the comment bubble when there are no approved comments.
765
+ printf(
766
+ '<span class="post-com-count post-com-count-no-comments"><span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
767
  $approved_comments_number,
768
  $pending_comments ? __( 'No approved comments' ) : __( 'No comments' )
769
  );
770
  }
771
 
772
  if ( $pending_comments ) {
773
+ printf(
774
+ '<a href="%s" class="post-com-count post-com-count-pending"><span class="comment-count-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
775
+ esc_url(
776
+ add_query_arg(
777
+ array(
778
+ 'p' => $post_id,
779
+ 'comment_status' => 'moderated',
780
+ ),
781
+ admin_url( 'edit-comments.php' )
782
+ )
783
+ ),
784
  $pending_comments_number,
785
  $pending_phrase
786
  );
787
  } else {
788
+ printf(
789
+ '<span class="post-com-count post-com-count-pending post-com-count-no-pending"><span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
790
  $pending_comments_number,
791
  $approved_comments ? __( 'No pending comments' ) : __( 'No comments' )
792
  );
794
  }
795
 
796
  /**
797
+ * Gets the current page number.
798
  *
799
  * @since 3.1.0
 
800
  *
801
  * @return int
802
  */
803
  public function get_pagenum() {
804
  $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
805
 
806
+ if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
807
  $pagenum = $this->_pagination_args['total_pages'];
808
+ }
809
 
810
  return max( 1, $pagenum );
811
  }
812
 
813
  /**
814
+ * Gets the number of items to display on a single page.
815
  *
816
  * @since 3.1.0
 
817
  *
818
  * @param string $option
819
  * @param int $default
821
  */
822
  protected function get_items_per_page( $option, $default = 20 ) {
823
  $per_page = (int) get_user_option( $option );
824
+ if ( empty( $per_page ) || $per_page < 1 ) {
825
  $per_page = $default;
826
+ }
827
 
828
  /**
829
  * Filters the number of items to be displayed on each page of the list table.
830
  *
831
+ * The dynamic hook name, `$option`, refers to the `per_page` option depending
832
+ * on the type of list table in use. Possible filter names include:
833
+ *
834
+ * - `edit_comments_per_page`
835
+ * - `sites_network_per_page`
836
+ * - `site_themes_network_per_page`
837
+ * - `themes_network_per_page'`
838
+ * - `users_network_per_page`
839
+ * - `edit_post_per_page`
840
+ * - `edit_page_per_page'`
841
+ * - `edit_{$post_type}_per_page`
842
+ * - `edit_post_tag_per_page`
843
+ * - `edit_category_per_page`
844
+ * - `edit_{$taxonomy}_per_page`
845
+ * - `site_users_network_per_page`
846
+ * - `users_per_page`
847
  *
848
  * @since 2.9.0
849
  *
853
  }
854
 
855
  /**
856
+ * Displays the pagination.
857
  *
858
  * @since 3.1.0
 
859
  *
860
  * @param string $which
861
  */
864
  return;
865
  }
866
 
867
+ $total_items = $this->_pagination_args['total_items'];
868
+ $total_pages = $this->_pagination_args['total_pages'];
869
  $infinite_scroll = false;
870
  if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
871
  $infinite_scroll = $this->_pagination_args['infinite_scroll'];
875
  $this->screen->render_screen_reader_content( 'heading_pagination' );
876
  }
877
 
878
+ $output = '<span class="displaying-num">' . sprintf(
879
+ /* translators: %s: Number of items. */
880
+ _n( '%s item', '%s items', $total_items ),
881
+ number_format_i18n( $total_items )
882
+ ) . '</span>';
883
 
884
+ $current = $this->get_pagenum();
885
  $removable_query_args = wp_removable_query_args();
886
 
887
  $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
893
  $total_pages_before = '<span class="paging-input">';
894
  $total_pages_after = '</span></span>';
895
 
896
+ $disable_first = false;
897
+ $disable_last = false;
898
+ $disable_prev = false;
899
+ $disable_next = false;
900
 
901
+ if ( 1 == $current ) {
902
  $disable_first = true;
903
+ $disable_prev = true;
904
+ }
905
+ if ( 2 == $current ) {
906
  $disable_first = true;
907
  }
908
+ if ( $total_pages == $current ) {
909
  $disable_last = true;
910
  $disable_next = true;
911
+ }
912
+ if ( $total_pages - 1 == $current ) {
913
  $disable_last = true;
914
  }
915
 
916
  if ( $disable_first ) {
917
+ $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
918
  } else {
919
+ $page_links[] = sprintf(
920
+ "<a class='first-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
921
  esc_url( remove_query_arg( 'paged', $current_url ) ),
922
  __( 'First page' ),
923
  '&laquo;'
925
  }
926
 
927
  if ( $disable_prev ) {
928
+ $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
929
  } else {
930
+ $page_links[] = sprintf(
931
+ "<a class='prev-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
932
+ esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
933
  __( 'Previous page' ),
934
  '&lsaquo;'
935
  );
939
  $html_current_page = $current;
940
  $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
941
  } else {
942
+ $html_current_page = sprintf(
943
+ "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
944
  '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
945
  $current,
946
  strlen( $total_pages )
947
  );
948
  }
949
  $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
950
+ $page_links[] = $total_pages_before . sprintf(
951
+ /* translators: 1: Current page, 2: Total pages. */
952
+ _x( '%1$s of %2$s', 'paging' ),
953
+ $html_current_page,
954
+ $html_total_pages
955
+ ) . $total_pages_after;
956
 
957
  if ( $disable_next ) {
958
+ $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
959
  } else {
960
+ $page_links[] = sprintf(
961
+ "<a class='next-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
962
+ esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ),
963
  __( 'Next page' ),
964
  '&rsaquo;'
965
  );
966
  }
967
 
968
  if ( $disable_last ) {
969
+ $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
970
  } else {
971
+ $page_links[] = sprintf(
972
+ "<a class='last-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
973
  esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
974
  __( 'Last page' ),
975
  '&raquo;'
980
  if ( ! empty( $infinite_scroll ) ) {
981
  $pagination_links_class .= ' hide-if-js';
982
  }
983
+ $output .= "\n<span class='$pagination_links_class'>" . implode( "\n", $page_links ) . '</span>';
984
 
985
  if ( $total_pages ) {
986
  $page_class = $total_pages < 2 ? ' one-page' : '';
993
  }
994
 
995
  /**
996
+ * Gets a list of columns.
997
+ *
998
+ * The format is:
999
+ * - `'internal-name' => 'Title'`
1000
  *
1001
  * @since 3.1.0
 
1002
  * @abstract
1003
  *
1004
  * @return array
1005
  */
1006
  public function get_columns() {
1007
+ die( 'function WP_List_Table::get_columns() must be overridden in a subclass.' );
1008
  }
1009
 
1010
  /**
1011
+ * Gets a list of sortable columns.
 
 
 
1012
  *
1013
+ * The format is:
1014
+ * - `'internal-name' => 'orderby'`
1015
+ * - `'internal-name' => array( 'orderby', 'asc' )` - The second element sets the initial sorting order.
1016
+ * - `'internal-name' => array( 'orderby', true )` - The second element makes the initial order descending.
1017
  *
1018
  * @since 3.1.0
 
1019
  *
1020
  * @return array
1021
  */
1027
  * Gets the name of the default primary column.
1028
  *
1029
  * @since 4.3.0
 
1030
  *
1031
  * @return string Name of the default primary column, in this case, an empty string.
1032
  */
1033
  protected function get_default_primary_column_name() {
1034
  $columns = $this->get_columns();
1035
+ $column = '';
1036
 
1037
  if ( empty( $columns ) ) {
1038
  return $column;
1053
  }
1054
 
1055
  /**
1056
+ * Public wrapper for WP_List_Table::get_default_primary_column_name().
1057
  *
1058
  * @since 4.4.0
1059
  *
1067
  * Gets the name of the primary column.
1068
  *
1069
  * @since 4.3.0
 
1070
  *
1071
  * @return string The name of the primary column.
1072
  */
1074
  $columns = get_column_headers( $this->screen );
1075
  $default = $this->get_default_primary_column_name();
1076
 
1077
+ // If the primary column doesn't exist,
1078
+ // fall back to the first non-checkbox column.
1079
  if ( ! isset( $columns[ $default ] ) ) {
1080
+ $default = self::get_default_primary_column_name();
1081
  }
1082
 
1083
  /**
1088
  * @param string $default Column name default for the specific list table, e.g. 'name'.
1089
  * @param string $context Screen ID for specific list table, e.g. 'plugins'.
1090
  */
1091
+ $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
1092
 
1093
  if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
1094
  $column = $default;
1098
  }
1099
 
1100
  /**
1101
+ * Gets a list of all, hidden, and sortable columns, with filter applied.
1102
  *
1103
  * @since 3.1.0
 
1104
  *
1105
  * @return array
1106
  */
1107
  protected function get_column_info() {
1108
+ // $_column_headers is already set / cached.
1109
  if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
1110
+ /*
1111
+ * Backward compatibility for `$_column_headers` format prior to WordPress 4.3.
1112
+ *
1113
+ * In WordPress 4.3 the primary column name was added as a fourth item in the
1114
+ * column headers property. This ensures the primary column name is included
1115
+ * in plugins setting the property directly in the three item format.
1116
+ */
1117
  $column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
1118
  foreach ( $this->_column_headers as $key => $value ) {
1119
  $column_headers[ $key ] = $value;
1123
  }
1124
 
1125
  $columns = get_column_headers( $this->screen );
1126
+ $hidden = get_hidden_columns( $this->screen );
1127
 
1128
  $sortable_columns = $this->get_sortable_columns();
1129
  /**
1130
  * Filters the list table sortable columns for a specific screen.
1131
  *
1132
  * The dynamic portion of the hook name, `$this->screen->id`, refers
1133
+ * to the ID of the current screen.
1134
  *
1135
+ * @since 3.1.0
1136
  *
1137
  * @param array $sortable_columns An array of sortable columns.
1138
  */
1140
 
1141
  $sortable = array();
1142
  foreach ( $_sortable as $id => $data ) {
1143
+ if ( empty( $data ) ) {
1144
  continue;
1145
+ }
1146
 
1147
  $data = (array) $data;
1148
+ if ( ! isset( $data[1] ) ) {
1149
  $data[1] = false;
1150
+ }
1151
 
1152
+ $sortable[ $id ] = $data;
1153
  }
1154
 
1155
+ $primary = $this->get_primary_column_name();
1156
  $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
1157
 
1158
  return $this->_column_headers;
1159
  }
1160
 
1161
  /**
1162
+ * Returns the number of visible columns.
1163
  *
1164
  * @since 3.1.0
 
1165
  *
1166
  * @return int
1167
  */
1168
  public function get_column_count() {
1169
  list ( $columns, $hidden ) = $this->get_column_info();
1170
+ $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
1171
  return count( $columns ) - count( $hidden );
1172
  }
1173
 
1174
  /**
1175
+ * Prints column headers, accounting for hidden and sortable columns.
1176
  *
1177
  * @since 3.1.0
 
 
 
1178
  *
1179
+ * @param bool $with_id Whether to set the ID attribute or not
1180
  */
1181
  public function print_column_headers( $with_id = true ) {
1182
  list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1198
 
1199
  if ( ! empty( $columns['cb'] ) ) {
1200
  static $cb_counter = 1;
1201
+ $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
1202
  . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
1203
  $cb_counter++;
1204
  }
1206
  foreach ( $columns as $column_key => $column_display_name ) {
1207
  $class = array( 'manage-column', "column-$column_key" );
1208
 
1209
+ if ( in_array( $column_key, $hidden, true ) ) {
1210
  $class[] = 'hidden';
1211
  }
1212
 
1213
+ if ( 'cb' === $column_key ) {
1214
  $class[] = 'check-column';
1215
+ } elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ), true ) ) {
1216
  $class[] = 'num';
1217
+ }
1218
 
1219
  if ( $column_key === $primary ) {
1220
  $class[] = 'column-primary';
1221
  }
1222
 
1223
+ if ( isset( $sortable[ $column_key ] ) ) {
1224
+ list( $orderby, $desc_first ) = $sortable[ $column_key ];
1225
 
1226
  if ( $current_orderby === $orderby ) {
1227
  $order = 'asc' === $current_order ? 'desc' : 'asc';
1228
+
1229
  $class[] = 'sorted';
1230
  $class[] = $current_order;
1231
  } else {
1232
+ $order = strtolower( $desc_first );
1233
+
1234
+ if ( ! in_array( $order, array( 'desc', 'asc' ), true ) ) {
1235
+ $order = $desc_first ? 'desc' : 'asc';
1236
+ }
1237
+
1238
  $class[] = 'sortable';
1239
+ $class[] = 'desc' === $order ? 'asc' : 'desc';
1240
  }
1241
 
1242
+ $column_display_name = sprintf(
1243
+ '<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
1244
+ esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ),
1245
+ $column_display_name
1246
+ );
1247
  }
1248
 
1249
+ $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
1250
  $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
1251
+ $id = $with_id ? "id='$column_key'" : '';
1252
 
1253
+ if ( ! empty( $class ) ) {
1254
+ $class = "class='" . implode( ' ', $class ) . "'";
1255
+ }
1256
 
1257
  echo "<$tag $scope $id $class>$column_display_name</$tag>";
1258
  }
1259
  }
1260
 
1261
  /**
1262
+ * Displays the table.
1263
  *
1264
  * @since 3.1.0
 
1265
  */
1266
  public function display() {
1267
  $singular = $this->_args['singular'];
1269
  $this->display_tablenav( 'top' );
1270
 
1271
  $this->screen->render_screen_reader_content( 'heading_list' );
1272
+ ?>
1273
  <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
1274
  <thead>
1275
  <tr>
1277
  </tr>
1278
  </thead>
1279
 
1280
+ <tbody id="the-list"
1281
+ <?php
1282
  if ( $singular ) {
1283
  echo " data-wp-lists='list:$singular'";
1284
+ }
1285
+ ?>
1286
+ >
1287
  <?php $this->display_rows_or_placeholder(); ?>
1288
  </tbody>
1289
 
1294
  </tfoot>
1295
 
1296
  </table>
1297
+ <?php
1298
  $this->display_tablenav( 'bottom' );
1299
  }
1300
 
1301
  /**
1302
+ * Gets a list of CSS classes for the WP_List_Table table tag.
1303
  *
1304
  * @since 3.1.0
 
1305
  *
1306
+ * @return string[] Array of CSS classes for the table tag.
1307
  */
1308
  protected function get_table_classes() {
1309
+ $mode = get_user_setting( 'posts_list_mode', 'list' );
1310
+
1311
+ $mode_class = esc_attr( 'table-view-' . $mode );
1312
+
1313
+ return array( 'widefat', 'fixed', 'striped', $mode_class, $this->_args['plural'] );
1314
  }
1315
 
1316
  /**
1317
+ * Generates the table navigation above or below the table
1318
  *
1319
  * @since 3.1.0
 
1320
  * @param string $which
1321
  */
1322
  protected function display_tablenav( $which ) {
1326
  ?>
1327
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
1328
 
1329
+ <?php if ( $this->has_items() ) : ?>
1330
  <div class="alignleft actions bulkactions">
1331
  <?php $this->bulk_actions( $which ); ?>
1332
  </div>
1333
+ <?php
1334
+ endif;
1335
  $this->extra_tablenav( $which );
1336
  $this->pagination( $which );
1337
+ ?>
1338
 
1339
  <br class="clear" />
1340
  </div>
1341
+ <?php
1342
  }
1343
 
1344
  /**
1345
+ * Extra controls to be displayed between bulk actions and pagination.
1346
  *
1347
  * @since 3.1.0
 
1348
  *
1349
  * @param string $which
1350
  */
1351
  protected function extra_tablenav( $which ) {}
1352
 
1353
  /**
1354
+ * Generates the tbody element for the list table.
1355
  *
1356
  * @since 3.1.0
 
1357
  */
1358
  public function display_rows_or_placeholder() {
1359
  if ( $this->has_items() ) {
1366
  }
1367
 
1368
  /**
1369
+ * Generates the table rows.
1370
  *
1371
  * @since 3.1.0
 
1372
  */
1373
  public function display_rows() {
1374
+ foreach ( $this->items as $item ) {
1375
  $this->single_row( $item );
1376
+ }
1377
  }
1378
 
1379
  /**
1380
+ * Generates content for a single row of the table.
1381
  *
1382
  * @since 3.1.0
 
1383
  *
1384
+ * @param object|array $item The current item
1385
  */
1386
  public function single_row( $item ) {
1387
  echo '<tr>';
1390
  }
1391
 
1392
  /**
1393
+ * @param object|array $item
 
1394
  * @param string $column_name
1395
  */
1396
  protected function column_default( $item, $column_name ) {}
1397
 
1398
  /**
1399
+ * @param object|array $item
 
1400
  */
1401
  protected function column_cb( $item ) {}
1402
 
1403
  /**
1404
+ * Generates the columns for a single row of the table.
1405
  *
1406
  * @since 3.1.0
 
1407
  *
1408
+ * @param object|array $item The current item.
1409
  */
1410
  protected function single_row_columns( $item ) {
1411
  list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1416
  $classes .= ' has-row-actions column-primary';
1417
  }
1418
 
1419
+ if ( in_array( $column_name, $hidden, true ) ) {
1420
  $classes .= ' hidden';
1421
  }
1422
 
1423
  // Comments column uses HTML in the display name with screen reader text.
1424
+ // Strip tags to get closer to a user-friendly string.
1425
+ $data = 'data-colname="' . esc_attr( wp_strip_all_tags( $column_display_name ) ) . '"';
1426
 
1427
  $attributes = "class='$classes' $data";
1428
 
1442
  echo "<td $attributes>";
1443
  echo call_user_func( array( $this, 'column_' . $column_name ), $item );
1444
  echo $this->handle_row_actions( $item, $column_name, $primary );
1445
+ echo '</td>';
1446
  } else {
1447
  echo "<td $attributes>";
1448
  echo $this->column_default( $item, $column_name );
1449
  echo $this->handle_row_actions( $item, $column_name, $primary );
1450
+ echo '</td>';
1451
  }
1452
  }
1453
  }
1456
  * Generates and display row actions links for the list table.
1457
  *
1458
  * @since 4.3.0
 
1459
  *
1460
+ * @param object|array $item The item being acted upon.
1461
+ * @param string $column_name Current column name.
1462
+ * @param string $primary Primary column name.
1463
+ * @return string The row actions HTML, or an empty string
1464
+ * if the current column is not the primary column.
1465
  */
1466
  protected function handle_row_actions( $item, $column_name, $primary ) {
1467
  return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
1468
+ }
1469
 
1470
  /**
1471
+ * Handles an incoming ajax request (called from admin-ajax.php)
1472
  *
1473
  * @since 3.1.0
 
1474
  */
1475
  public function ajax_response() {
1476
  $this->prepare_items();
1488
 
1489
  if ( isset( $this->_pagination_args['total_items'] ) ) {
1490
  $response['total_items_i18n'] = sprintf(
1491
+ /* translators: Number of items. */
1492
  _n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1493
  number_format_i18n( $this->_pagination_args['total_items'] )
1494
  );
1495
  }
1496
  if ( isset( $this->_pagination_args['total_pages'] ) ) {
1497
+ $response['total_pages'] = $this->_pagination_args['total_pages'];
1498
  $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1499
  }
1500
 
1502
  }
1503
 
1504
  /**
1505
+ * Sends required variables to JavaScript land.
1506
  *
1507
+ * @since 3.1.0
1508
  */
1509
  public function _js_vars() {
1510
  $args = array(
1512
  'screen' => array(
1513
  'id' => $this->screen->id,
1514
  'base' => $this->screen->base,
1515
+ ),
1516
  );
1517
 
1518
  printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=G87A9
4
  Tags: form, forms, contact form, contact forms, form, forms, form to email, email form, email, input, validation, jquery, shortcode, form builder, contact form builder, form manager, form creator
5
  Requires at least: 4.7
6
  Tested up to: 5.8.1
7
- Stable tag: 3.0.4
8
  License: GPLv2 or later
9
 
10
  Build beautiful, fully functional contact forms in only a few minutes without writing PHP, CSS, or HTML.
@@ -231,6 +231,12 @@ function my_scripts_method() {
231
 
232
  == Changelog ==
233
 
 
 
 
 
 
 
234
  **Version 3.0.4 - Sep 21, 2021**
235
 
236
  * Fix security vulnerability when saving Form Name
4
  Tags: form, forms, contact form, contact forms, form, forms, form to email, email form, email, input, validation, jquery, shortcode, form builder, contact form builder, form manager, form creator
5
  Requires at least: 4.7
6
  Tested up to: 5.8.1
7
+ Stable tag: 3.0.5
8
  License: GPLv2 or later
9
 
10
  Build beautiful, fully functional contact forms in only a few minutes without writing PHP, CSS, or HTML.
231
 
232
  == Changelog ==
233
 
234
+ **Version 3.0.5 - Oct 21, 2021**
235
+
236
+ * Update some URLs to no longer rely on query params
237
+ * Update class list wrapper
238
+ * Escape additional data
239
+
240
  **Version 3.0.4 - Sep 21, 2021**
241
 
242
  * Fix security vulnerability when saving Form Name
visual-form-builder.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Visual Form Builder
4
  Plugin URI: https://wordpress.org/plugins/visual-form-builder/
5
  Description: Dynamically build forms using a simple interface. Forms include jQuery validation, a basic logic-based verification system, and entry tracking.
6
- Version: 3.0.4
7
  Author: Matthew Muro
8
  Author URI: http://vfbpro.com
9
  Text Domain: visual-form-builder
@@ -26,7 +26,7 @@ class Visual_Form_Builder {
26
  * The current version of the plugin.
27
  * @var [type]
28
  */
29
- protected $version = '3.0.4';
30
 
31
  /**
32
  * The current DB version. Used if we need to update the DB later.
3
  Plugin Name: Visual Form Builder
4
  Plugin URI: https://wordpress.org/plugins/visual-form-builder/
5
  Description: Dynamically build forms using a simple interface. Forms include jQuery validation, a basic logic-based verification system, and entry tracking.
6
+ Version: 3.0.5
7
  Author: Matthew Muro
8
  Author URI: http://vfbpro.com
9
  Text Domain: visual-form-builder
26
  * The current version of the plugin.
27
  * @var [type]
28
  */
29
+ protected $version = '3.0.5';
30
 
31
  /**
32
  * The current DB version. Used if we need to update the DB later.