PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.4.5
GiveWP – Donation Plugin and Fundraising Platform v2.4.5
4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / includes / admin / donors / class-donor-table.php
give / includes / admin / donors Last commit date
class-donor-table.php 7 years ago donor-actions.php 7 years ago donor-functions.php 7 years ago donors.php 7 years ago
class-donor-table.php
619 lines
1 <?php
2 /**
3 * Donor List Table Class.
4 *
5 * The list view under WP-Admin > Donations > Donors.
6 *
7 * @package Give
8 * @subpackage Admin/Reports
9 * @copyright Copyright (c) 2016, GiveWP
10 * @license https://opensource.org/licenses/gpl-license GNU Public License
11 * @since 1.0
12 */
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 // Load WP_List_Table if not loaded.
20 if ( ! class_exists( 'WP_List_Table' ) ) {
21 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
22 }
23
24 /**
25 * Give_Donor_List_Table Class.
26 *
27 * @since 1.0
28 */
29 class Give_Donor_List_Table extends WP_List_Table {
30
31 /**
32 * Number of items per page.
33 *
34 * @var int
35 * @since 1.0
36 */
37 public $per_page = 30;
38
39 /**
40 * Number of donors found.
41 *
42 * @var int
43 * @since 1.0
44 */
45 public $count = 0;
46
47 /**
48 * Total donors.
49 *
50 * @var int
51 * @since 1.0
52 */
53 public $total = 0;
54
55 /**
56 * Get things started.
57 *
58 * @since 1.0
59 * @see WP_List_Table::__construct()
60 */
61 public function __construct() {
62 // Set parent defaults.
63 parent::__construct( array(
64 'singular' => __( 'Donor', 'give' ), // Singular name of the listed records.
65 'plural' => __( 'Donors', 'give' ), // Plural name of the listed records.
66 'ajax' => false, // Does this table support ajax?.
67 ) );
68
69 }
70 /**
71 * Add donors search filter.
72 *
73 * @since 2.4.0
74 * @return void
75 */
76 public function advanced_filters() {
77 $start_date = isset( $_GET['start-date'] ) ? strtotime( give_clean( $_GET['start-date'] ) ) : '';
78 $end_date = isset( $_GET['end-date'] ) ? strtotime( give_clean( $_GET['end-date'] ) ) : '';
79 $status = isset( $_GET['status'] ) ? give_clean( $_GET['status'] ) : '';
80 $donor = isset( $_GET['donor'] ) ? absint( $_GET['donor'] ) : '';
81 $search = $this->get_search();
82 $form_id = ! empty( $_GET['form_id'] ) ? absint( $_GET['form_id'] ) : 0;
83 ?>
84 <div id="give-donor-filters" class="give-filters">
85 <div class="give-donor-search-box">
86 <input type="text" id="give-donors-search-input" placeholder="<?php _e( 'Name, Email, or Donor ID', 'give' ); ?>" name="s" value="<?php echo $search; ?>">
87 <?php submit_button( __( 'Search', 'give' ), 'button', false, false, array(
88 'ID' => 'donor-search-submit',
89 ) ); ?>
90 </div>
91 <div class="give-filter give-filter-half">
92 <label for="start-date"
93 class="give-start-date-label"><?php _e( 'Start Date', 'give' ); ?></label>
94 <input type="text"
95 id="start-date"
96 name="start-date"
97 class="give_datepicker"
98 autocomplete="off"
99 value="<?php echo $start_date ? date_i18n( give_date_format(), $start_date ) : ''; ?>"
100 data-standard-date="<?php echo $start_date ? date( 'Y-m-d', $start_date ) : $start_date; ?>"
101 placeholder="<?php _e( 'Start Date', 'give' ); ?>"
102 />
103 </div>
104 <div class="give-filter give-filter-half">
105 <label for="end-date" class="give-end-date-label"><?php _e( 'End Date', 'give' ); ?></label>
106 <input type="text"
107 id="end-date"
108 name="end-date"
109 class="give_datepicker"
110 autocomplete="off"
111 value="<?php echo $end_date ? date_i18n( give_date_format(), $end_date ) : ''; ?>"
112 data-standard-date="<?php echo $end_date ? date( 'Y-m-d', $end_date ) : $end_date; ?>"
113 placeholder="<?php _e( 'End Date', 'give' ); ?>"
114 />
115 </div>
116 <div id="give-payment-form-filter" class="give-filter">
117 <label for="give-donation-forms-filter"
118 class="give-donation-forms-filter-label"><?php _e( 'Form', 'give' ); ?></label>
119 <?php
120 // Filter Donations by Donation Forms.
121 echo Give()->html->forms_dropdown(
122 array(
123 'name' => 'form_id',
124 'id' => 'give-donation-forms-filter',
125 'class' => 'give-donation-forms-filter',
126 'selected' => $form_id, // Make sure to have $form_id set to 0, if there is no selection.
127 'chosen' => true,
128 'number' => 30,
129 )
130 );
131 ?>
132 </div>
133
134 <?php
135 /**
136 * Action to add hidden fields and HTML in donor search.
137 *
138 * @since 2.4.0
139 */
140 do_action( 'give_donor_table_advanced_filters' );
141
142
143 if ( ! empty( $status ) ) {
144 echo sprintf( '<input type="hidden" name="status" value="%s"/>', esc_attr( $status ) );
145 }
146
147 if ( ! empty( $donor ) ) {
148 echo sprintf( '<input type="hidden" name="donor" value="%s"/>', absint( $donor ) );
149 }
150 ?>
151
152 <div class="give-filter">
153 <?php submit_button( __( 'Apply', 'give' ), 'secondary', '', false ); ?>
154 <?php
155 // Clear active filters button.
156 if ( ! empty( $start_date ) || ! empty( $end_date ) || ! empty( $donor ) || ! empty( $search ) || ! empty( $status ) || ! empty( $form_id ) ) :
157 ?>
158 <a href="<?php echo admin_url( 'edit.php?post_type=give_forms&page=give-donors' ); ?>"
159 class="button give-clear-filters-button"><?php _e( 'Clear Filters', 'give' ); ?></a>
160 <?php endif; ?>
161 </div>
162 </div>
163
164 <?php
165 }
166
167 /**
168 * This function renders most of the columns in the list table.
169 *
170 * @param array $donor Contains all the data of the donors.
171 * @param string $column_name The name of the column.
172 *
173 * @access public
174 * @since 1.0
175 *
176 * @return string Column Name.
177 */
178 public function column_default( $donor, $column_name ) {
179
180 switch ( $column_name ) {
181
182 case 'num_donations' :
183 $value = sprintf(
184 '<a href="%s">%s</a>',
185 admin_url( 'edit.php?post_type=give_forms&page=give-payment-history&donor=' . absint( $donor['id'] ) ),
186 esc_html( $donor['num_donations'] )
187 );
188 break;
189
190 case 'amount_spent' :
191 $value = give_currency_filter( give_format_amount( $donor[ $column_name ], array( 'sanitize' => false ) ) );
192 break;
193
194 case 'date_created' :
195 $value = date_i18n( give_date_format(), strtotime( $donor['date_created'] ) );
196 break;
197
198 default:
199 $value = isset( $donor[ $column_name ] ) ? $donor[ $column_name ] : null;
200 break;
201 }
202
203 return apply_filters( "give_donors_column_{$column_name}", $value, $donor['id'] );
204
205 }
206
207 /**
208 * For CheckBox Column
209 *
210 * @param array $donor Donor Data.
211 *
212 * @access public
213 * @since 1.8.16
214 *
215 * @return string
216 */
217 public function column_cb( $donor ) {
218 return sprintf(
219 '<input class="donor-selector" type="checkbox" name="donor[]" value="%1$d" data-name="%2$s" />',
220 $donor['id'],
221 $donor['name']
222 );
223 }
224
225 /**
226 * Column name.
227 *
228 * @param array $donor Donor Data.
229 *
230 * @access public
231 * @since 1.0
232 *
233 * @return string
234 */
235 public function column_name( $donor ) {
236
237 // Get donor's initials for non-gravatars
238 $title_prefix = Give()->donor_meta->get_meta( $donor['id'], '_give_donor_title_prefix', true );
239 $donor_name_without_prefix = trim( str_replace( $title_prefix, '', $donor['name'] ) );
240 $donor_name_array = explode( " ", $donor_name_without_prefix );
241 $donor_name_args['firstname'] = ! empty( $donor_name_array[0] ) ? $donor_name_array[0] : '';
242 $donor_name_args['lastname'] = ! empty( $donor_name_array[1] ) ? $donor_name_array[1] : '';
243 $donor_name_initial = give_get_name_initial( $donor_name_args );
244
245 $donation_gravatar_image = sprintf(
246 '<span class="give-donor__image give-donor-admin-avatar" data-donor_email="%1$s" data-has-valid-gravatar="%2$s">%3$s</span>',
247 md5( strtolower( trim( $donor['email'] ) ) ),
248 absint( give_validate_gravatar( $donor['email'] ) ),
249 $donor_name_initial
250 );
251
252 $name = ! empty( $donor['name'] ) ? ( $donation_gravatar_image . '<span class="give-donor-name-text">' . $donor['name'] . '</span>' ) : '<em>' . __( 'Unnamed Donor', 'give' ) . '</em>';
253 $view_url = admin_url( 'edit.php?post_type=give_forms&page=give-donors&view=overview&id=' . $donor['id'] );
254 $actions = $this->get_row_actions( $donor );
255
256 return '<a href="' . esc_url( $view_url ) . '" class="give-donor-name">' . $name . '</a>' . $this->row_actions( $actions );
257 }
258
259 /**
260 * Retrieve the table columns.
261 *
262 * @access public
263 * @since 1.0
264 *
265 * @return array $columns Array of all the list table columns.
266 */
267 public function get_columns() {
268 $columns = array(
269 'cb' => '<input type="checkbox" />', // Render a checkbox instead of text.
270 'name' => __( 'Name', 'give' ),
271 'email' => __( 'Email', 'give' ),
272 'num_donations' => __( 'Donations', 'give' ),
273 'amount_spent' => __( 'Total Donated', 'give' ),
274 'date_created' => __( 'Date Created', 'give' ),
275 );
276
277 return apply_filters( 'give_list_donors_columns', $columns );
278
279 }
280
281 /**
282 * Get the sortable columns.
283 *
284 * @access public
285 * @since 2.1
286 * @return array Array of all the sortable columns.
287 */
288 public function get_sortable_columns() {
289
290 $columns = array(
291 'date_created' => array( 'date_created', true ),
292 'name' => array( 'name', true ),
293 'num_donations' => array( 'purchase_count', false ),
294 'amount_spent' => array( 'purchase_value', false ),
295 );
296
297 return apply_filters( 'give_list_donors_sortable_columns', $columns );
298 }
299
300 /**
301 * Retrieve row actions.
302 *
303 * @param array $donor Donor Data.
304 *
305 * @since 1.7
306 * @access public
307 *
308 * @return array An array of action links.
309 */
310 public function get_row_actions( $donor ) {
311
312 $actions = array(
313 'id' => '<span class="give-donor-id">ID: ' . $donor['id'] . ' </span>',
314 'view' => sprintf( '<a href="%1$s" aria-label="%2$s">%3$s</a>', admin_url( 'edit.php?post_type=give_forms&page=give-donors&view=overview&id=' . $donor['id'] ), sprintf( esc_attr__( 'View "%s"', 'give' ), $donor['name'] ), __( 'View Donor', 'give' ) ),
315 'delete' => sprintf( '<a class="%1$s" data-id="%2$s" href="#" aria-label="%3$s">%4$s</a>', 'give-single-donor-delete', $donor['id'], sprintf( esc_attr__( 'Delete "%s"', 'give' ), $donor['name'] ), __( 'Delete', 'give' ) ),
316 );
317
318 return apply_filters( 'give_donor_row_actions', $actions, $donor );
319
320 }
321
322 /**
323 * Retrieve the current page number.
324 *
325 * @access public
326 * @since 1.0
327 *
328 * @return int Current page number.
329 */
330 public function get_paged() {
331 return isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1;
332 }
333
334 /**
335 * Retrieves the search query string.
336 *
337 * @access public
338 * @since 1.0
339 *
340 * @return mixed string If search is present, false otherwise.
341 */
342 public function get_search() {
343 return ! empty( $_GET['s'] ) ? urldecode( trim( $_GET['s'] ) ) : false;
344 }
345
346 /**
347 * Get the Bulk Actions.
348 *
349 * @access public
350 * @since 1.8.16
351 *
352 * @return array
353 */
354 public function get_bulk_actions() {
355 $actions = array(
356 'delete' => __( 'Delete', 'give' ),
357 );
358
359 return $actions;
360 }
361
362 /**
363 * Generate the table navigation above or below the table
364 *
365 * @param string $which Position to trigger i.e. Top/Bottom.
366 *
367 * @access protected
368 * @since 1.8.16
369 */
370 protected function display_tablenav( $which ) {
371 if ( 'top' === $which ) {
372 wp_nonce_field( 'bulk-donors','_wpnonce', false );
373 }
374 ?>
375 <div class="tablenav <?php echo esc_attr( $which ); ?>">
376 <?php if ( $this->has_items() ) : ?>
377 <div class="alignleft actions bulkactions">
378 <?php $this->bulk_actions( $which ); ?>
379 </div>
380 <?php endif;
381 $this->extra_tablenav( $which );
382 $this->pagination( $which );
383 ?>
384 <br class="clear"/>
385 </div>
386 <?php
387 }
388
389 /**
390 * Retrieves the donor data from db.
391 *
392 * @access public
393 * @since 1.0
394 *
395 * @return array $data The Donor data.
396 */
397 public function donor_data() {
398
399 $data = array();
400
401 // Get donor query.
402 $args = $this->get_donor_query();
403 $donors = Give()->donors->get_donors( $args );
404
405 if ( $donors ) {
406
407 foreach ( $donors as $donor ) {
408
409 $user_id = ! empty( $donor->user_id ) ? intval( $donor->user_id ) : 0;
410 $title_prefix = Give()->donor_meta->get_meta( $donor->id, '_give_donor_title_prefix', true );
411
412 // If title prefix is set, then update the donor name.
413 $donor->name = give_get_donor_name_with_title_prefixes( $title_prefix, $donor->name );
414
415 $data[] = array(
416 'id' => $donor->id,
417 'user_id' => $user_id,
418 'name' => $donor->name,
419 'email' => $donor->email,
420 'num_donations' => $donor->purchase_count,
421 'amount_spent' => $donor->purchase_value,
422 'date_created' => $donor->date_created,
423 );
424 }
425 }
426
427 return apply_filters( 'give_donors_column_query_data', $data );
428 }
429
430 /**
431 * Get donor count.
432 *
433 * @since 1.8.1
434 * @access private
435 */
436 private function get_donor_count() {
437 // Get donor query.
438 $_donor_query = $this->get_donor_query();
439
440 $_donor_query['number'] = - 1;
441 $_donor_query['offset'] = 0;
442 $donors = Give()->donors->get_donors( $_donor_query );
443
444 return count( $donors );
445 }
446
447 /**
448 * Get donor query.
449 *
450 * @since 1.8.1
451 * @access public
452 *
453 * @return array
454 */
455 public function get_donor_query() {
456 $per_page = $this->per_page;
457 $paged = $this->get_paged();
458 $donor = isset( $_GET['donor'] ) ? $_GET['donor'] : null;
459 $start_date = ! empty ( $_GET['start-date'] ) ? strtotime( give_clean( $_GET['start-date'] ) ) : false;
460 $end_date = ! empty( $_GET['end-date'] ) ? strtotime( give_clean( $_GET['end-date'] ) ) : false;
461 $form_id = ! empty( $_GET['form_id'] ) ? absint( $_GET['form_id'] ) : null;
462 $offset = $this->per_page * ( $paged - 1 );
463 $search = $this->get_search();
464 $order = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 'DESC';
465 $orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : 'id';
466
467 $args = array(
468 'output' => 'payments',
469 'number' => $per_page,
470 'offset' => $offset,
471 'page' => isset( $_GET['paged'] ) ? $_GET['paged'] : null,
472 'orderby' => $orderby,
473 'order' => $order,
474 'donor' => $donor,
475 's' => $search,
476 'start_date' => $start_date,
477 'end_date' => $end_date,
478 'give_forms' => $form_id,
479 );
480
481 /**
482 * Filter to modify donor table argument.
483 *
484 * @since 2.4.0
485 */
486 $args = (array) apply_filters( 'give_donor_table_query', $args );
487
488 return $args;
489 }
490
491 /**
492 * Generates content for a single row of the table
493 *
494 * @param object $item The current item.
495 *
496 * @since 1.8.17
497 * @access public
498 */
499 public function single_row( $item ) {
500 echo sprintf( '<tr id="donor-%1$d" data-id="%2$d" data-name="%3$s">', $item['id'], $item['id'], $item['name'] );
501 $this->single_row_columns( $item );
502 echo '</tr>';
503 }
504
505 /**
506 * Display the final donor table
507 *
508 * @since 1.8.17
509 * @access public
510 */
511 public function display() {
512 $singular = $this->_args['singular'];
513
514 $this->display_tablenav( 'top' );
515
516 $this->screen->render_screen_reader_content( 'heading_list' );
517
518 $get_data = give_clean( $_GET ); // WPCS: input var ok, sanitization ok, CSRF ok.
519
520 $order = ! empty( $get_data['order'] ) ? $get_data['order'] : 'DESC';
521 $order_by = ! empty( $get_data['orderby'] ) ? $get_data['orderby'] : 'id';
522 ?>
523 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
524 <thead>
525 <tr>
526 <?php $this->print_column_headers(); ?>
527 </tr>
528 </thead>
529
530 <tbody id="the-list"<?php
531 if ( $singular ) {
532 echo " data-wp-lists='list:$singular'";
533 } ?>>
534 <tr class="hidden"></tr>
535 <tr id="give-bulk-delete"
536 class="inline-edit-row inline-edit-row-page inline-edit-page bulk-edit-row bulk-edit-row-page bulk-edit-page inline-editor"
537 style="display: none;">
538 <td colspan="6" class="colspanchange">
539
540 <fieldset class="inline-edit-col-left">
541 <legend class="inline-edit-legend"><?php esc_attr_e( 'BULK DELETE', 'give' ); ?></legend>
542 <div class="inline-edit-col">
543 <div id="bulk-titles">
544 <div id="give-bulk-donors" class="give-bulk-donors">
545
546 </div>
547 </div>
548 </fieldset>
549
550 <fieldset class="inline-edit-col-right">
551 <div class="inline-edit-col">
552 <label>
553 <input class="give-donor-delete-confirm" type="checkbox"
554 name="give-donor-delete-confirm"/>
555 <?php esc_attr_e( 'Are you sure you want to delete the selected donor(s)?', 'give' ); ?>
556 </label>
557 <label>
558 <input class="give-donor-delete-records" type="checkbox"
559 name="give-donor-delete-records"/>
560 <?php esc_attr_e( 'Delete all associated donations and records?', 'give' ); ?>
561 </label>
562 </div>
563 </fieldset>
564
565 <p class="submit inline-edit-save">
566 <input type="hidden" name="give_action" value="delete_bulk_donor"/>
567 <input type="hidden" name="orderby" value="<?php echo esc_html( $order_by ); ?>"/>
568 <input type="hidden" name="order" value="<?php echo esc_html( $order ); ?>"/>
569 <button type="button" id="give-bulk-delete-cancel"
570 class="button cancel alignleft"><?php esc_attr_e( 'Cancel', 'give' ); ?></button>
571 <input type="submit" id="give-bulk-delete-button" disabled
572 class="button button-primary alignright"
573 value="<?php esc_attr_e( 'Delete', 'give' ); ?>">
574 <br class="clear">
575 </p>
576 </td>
577 </tr>
578 <?php $this->display_rows_or_placeholder(); ?>
579 </tbody>
580
581 <tfoot>
582 <tr>
583 <?php $this->print_column_headers( false ); ?>
584 </tr>
585 </tfoot>
586
587 </table>
588 <?php
589 $this->display_tablenav( 'bottom' );
590 }
591
592 /**
593 * Setup the final data for the table.
594 *
595 * @access public
596 * @since 1.0
597 *
598 * @return void
599 */
600 public function prepare_items() {
601
602 $columns = $this->get_columns();
603 $hidden = array(); // No hidden columns.
604 $sortable = $this->get_sortable_columns();
605
606 $this->_column_headers = array( $columns, $hidden, $sortable );
607
608 $this->items = $this->donor_data();
609
610 $this->total = $this->get_donor_count();
611
612 $this->set_pagination_args( array(
613 'total_items' => $this->total,
614 'per_page' => $this->per_page,
615 'total_pages' => ceil( $this->total / $this->per_page ),
616 ) );
617 }
618 }
619