Changeset 2898 for sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-admin.php
- Timestamp:
- 04/05/2016 04:05:02 PM (8 years ago)
- Location:
- sites/trunk/wordcamp.org
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordcamp.org
-
Property
svn:mergeinfo
set to
/sites/branches/application-tracking/wordcamp.org merged eligible
-
Property
svn:mergeinfo
set to
-
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-admin.php
r2517 r2898 15 15 16 16 /** 17 * wcpt_admin ()18 *19 17 * Initialize WCPT Admin 20 18 */ 21 function WordCamp_Admin() {19 function __construct() { 22 20 $this->active_admin_notices = array(); 23 21 … … 27 25 // Forum column headers. 28 26 add_filter( 'manage_' . WCPT_POST_TYPE_ID . '_posts_columns', array( $this, 'column_headers' ) ); 27 add_filter( 'display_post_states', array( $this, 'display_post_states' ) ); 29 28 30 29 // Forum columns (in page row) … … 33 32 34 33 // Topic metabox actions 35 add_action( 'ad min_menu',array( $this, 'metabox' ) );36 add_action( 'save_post', array( $this, 'metabox_save' ) );34 add_action( 'add_meta_boxes', array( $this, 'metabox' ) ); 35 add_action( 'save_post', array( $this, 'metabox_save' ), 10, 2 ); 37 36 38 37 // Scripts and CSS … … 43 42 // Post status transitions 44 43 add_action( 'transition_post_status', array( $this, 'trigger_schedule_actions' ), 10, 3 ); 44 add_action( 'transition_post_status', array( $this, 'log_status_changes' ), 10, 3 ); 45 45 add_action( 'wcpt_added_to_planning_schedule', array( $this, 'add_organizer_to_central' ), 10 ); 46 46 add_action( 'wcpt_added_to_planning_schedule', array( $this, 'mark_date_added_to_planning_schedule' ), 10 ); 47 add_filter( 'wp_insert_post_data', array( $this, 'enforce_post_status _progression' ), 10, 2 );48 add_filter( 'wp_insert_post_data', array( $this, 'require_complete_meta_to_publish_wordcamp' ), 1 0, 2 );47 add_filter( 'wp_insert_post_data', array( $this, 'enforce_post_status' ), 10, 2 ); 48 add_filter( 'wp_insert_post_data', array( $this, 'require_complete_meta_to_publish_wordcamp' ), 11, 2 ); // after enforce_post_status 49 49 50 50 // Admin notices … … 87 87 'high' 88 88 ); 89 90 add_meta_box( 91 'wcpt_original_application', 92 'Original Application', 93 array( $this, 'original_application_metabox' ), 94 WCPT_POST_TYPE_ID, 95 'advanced', 96 'low' 97 ); 98 99 // Notes are private, so only show them to network admins 100 if ( current_user_can( 'manage_network' ) ) { 101 add_meta_box( 102 'wcpt_notes', 103 __( 'Add a Note', 'wordcamporg' ), 104 'wcpt_add_note_metabox', 105 WCPT_POST_TYPE_ID, 106 'side', 107 'low' 108 ); 109 110 add_meta_box( 111 'wcpt_log', 112 'Log', 113 'wcpt_log_metabox', 114 WCPT_POST_TYPE_ID, 115 'advanced', 116 'low' 117 ); 118 } 119 120 // Remove core's submitdiv. 121 remove_meta_box( 'submitdiv', WCPT_POST_TYPE_ID, 'side' ); 122 123 $statuses = WordCamp_Loader::get_post_statuses(); 124 125 add_meta_box( 126 'submitdiv', 127 __( 'Status', 'wordcamporg' ), 128 array( $this, 'metabox_status' ), 129 WCPT_POST_TYPE_ID, 130 'side', 131 'high' 132 ); 89 133 } 90 134 … … 97 141 * @return int 98 142 */ 99 function metabox_save( $post_id ) { 100 143 function metabox_save( $post_id, $post ) { 101 144 // Don't add/remove meta on revisions and auto-saves 102 145 if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) … … 110 153 // WordCamp post type only 111 154 if ( WCPT_POST_TYPE_ID != get_post_type() ) { 155 return; 156 } 157 158 // Make sure the requset came from the edit post screen. 159 if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'update-post_' . $post_id ) ) { 112 160 return; 113 161 } … … 128 176 // Loop through meta keys and update 129 177 foreach ( $wcpt_meta_keys as $key => $value ) { 130 131 178 // Get post value 132 179 $post_value = wcpt_key_to_str( $key, 'wcpt_' ); … … 159 206 } 160 207 } 208 209 $this->validate_and_add_note( $post_id ); 210 } 211 212 /** 213 * Validate and add a new note 214 * 215 * @param int $post_id 216 */ 217 protected function validate_and_add_note( $post_id ) { 218 check_admin_referer( 'wcpt_notes', 'wcpt_notes_nonce' ); 219 220 $new_note_message = sanitize_text_field( wp_unslash( $_POST['wcpt_new_note'] ) ); 221 222 if ( empty( $new_note_message ) ) { 223 return; 224 } 225 226 add_post_meta( $post_id, '_note', array( 227 'timestamp' => time(), 228 'user_id' => get_current_user_id(), 229 'message' => $new_note_message, 230 ) ); 161 231 } 162 232 … … 197 267 */ 198 268 function meta_keys( $meta_group = '' ) { 199 200 269 /* 201 270 * Warning: These keys are used for both the input field label and the postmeta key, so if you want to … … 212 281 case 'organizer': 213 282 $retval = array ( 214 'Organizer Name' => 'text',215 'WordPress.org Username' => 'text',216 'Email Address' => 'text',// Note: This is the lead organizer's e-mail address, which is different than the "E-mail Address" field217 'Telephone' => 'text',218 'Mailing Address' => 'textarea',219 'Sponsor Wrangler Name' => 'text',220 'Sponsor Wrangler E-mail Address' => 'text',221 'Budget Wrangler Name' => 'text',222 'Budget Wrangler E-mail Address' => 'text',223 'Venue Wrangler Name' => 'text',224 'Venue Wrangler E-mail Address' => 'text',225 'Speaker Wrangler Name' => 'text',226 'Speaker Wrangler E-mail Address' => 'text',227 'Food/Beverage Wrangler Name' => 'text',228 'Food/Beverage Wrangler E-mail Address' => 'text',229 'Swag Wrangler Name' => 'text',230 'Swag Wrangler E-mail Address' => 'text',231 'Volunteer Wrangler Name' => 'text',232 'Volunteer Wrangler E-mail Address' => 'text',233 'Printing Wrangler Name' => 'text',234 'Printing Wrangler E-mail Address' => 'text',235 'Design Wrangler Name' => 'text',236 'Design Wrangler E-mail Address' => 'text',237 'Website Wrangler Name' => 'text',238 'Website Wrangler E-mail Address' => 'text',239 'Social Media/Publicity Wrangler Name' 240 'Social Media/Publicity Wrangler E-mail Address' 241 'A/V Wrangler Name' => 'text',242 'A/V Wrangler E-mail Address' => 'text',243 'Party Wrangler Name' => 'text',244 'Party Wrangler E-mail Address' => 'text',245 'Travel Wrangler Name' => 'text',246 'Travel Wrangler E-mail Address' => 'text',247 'Safety Wrangler Name' => 'text',248 'Safety Wrangler E-mail Address' => 'text',249 'Mentor Name' => 'text',250 'Mentor E-mail Address' => 'text',283 'Organizer Name' => 'text', 284 'WordPress.org Username' => 'text', 285 'Email Address' => 'text', // Note: This is the lead organizer's e-mail address, which is different than the "E-mail Address" field 286 'Telephone' => 'text', 287 'Mailing Address' => 'textarea', 288 'Sponsor Wrangler Name' => 'text', 289 'Sponsor Wrangler E-mail Address' => 'text', 290 'Budget Wrangler Name' => 'text', 291 'Budget Wrangler E-mail Address' => 'text', 292 'Venue Wrangler Name' => 'text', 293 'Venue Wrangler E-mail Address' => 'text', 294 'Speaker Wrangler Name' => 'text', 295 'Speaker Wrangler E-mail Address' => 'text', 296 'Food/Beverage Wrangler Name' => 'text', 297 'Food/Beverage Wrangler E-mail Address' => 'text', 298 'Swag Wrangler Name' => 'text', 299 'Swag Wrangler E-mail Address' => 'text', 300 'Volunteer Wrangler Name' => 'text', 301 'Volunteer Wrangler E-mail Address' => 'text', 302 'Printing Wrangler Name' => 'text', 303 'Printing Wrangler E-mail Address' => 'text', 304 'Design Wrangler Name' => 'text', 305 'Design Wrangler E-mail Address' => 'text', 306 'Website Wrangler Name' => 'text', 307 'Website Wrangler E-mail Address' => 'text', 308 'Social Media/Publicity Wrangler Name' => 'text', 309 'Social Media/Publicity Wrangler E-mail Address' => 'text', 310 'A/V Wrangler Name' => 'text', 311 'A/V Wrangler E-mail Address' => 'text', 312 'Party Wrangler Name' => 'text', 313 'Party Wrangler E-mail Address' => 'text', 314 'Travel Wrangler Name' => 'text', 315 'Travel Wrangler E-mail Address' => 'text', 316 'Safety Wrangler Name' => 'text', 317 'Safety Wrangler E-mail Address' => 'text', 318 'Mentor Name' => 'text', 319 'Mentor E-mail Address' => 'text', 251 320 ); 252 321 … … 292 361 'Multi-Event Sponsor Region' => 'mes-dropdown', 293 362 294 'Organizer Name' => 'text',295 'WordPress.org Username' => 'text',296 'Email Address' => 'text',297 'Telephone' => 'text',298 'Mailing Address' => 'textarea',299 'Sponsor Wrangler Name' => 'text',300 'Sponsor Wrangler E-mail Address' => 'text',301 'Budget Wrangler Name' => 'text',302 'Budget Wrangler E-mail Address' => 'text',303 'Venue Wrangler Name' => 'text',304 'Venue Wrangler E-mail Address' => 'text',305 'Speaker Wrangler Name' => 'text',306 'Speaker Wrangler E-mail Address' => 'text',307 'Food/Beverage Wrangler Name' => 'text',308 'Food/Beverage Wrangler E-mail Address' => 'text',309 'Swag Wrangler Name' => 'text',310 'Swag Wrangler E-mail Address' => 'text',311 'Volunteer Wrangler Name' => 'text',312 'Volunteer Wrangler E-mail Address' => 'text',313 'Printing Wrangler Name' => 'text',314 'Printing Wrangler E-mail Address' => 'text',315 'Design Wrangler Name' => 'text',316 'Design Wrangler E-mail Address' => 'text',317 'Website Wrangler Name' => 'text',318 'Website Wrangler E-mail Address' => 'text',319 'Social Media/Publicity Wrangler Name' 320 'Social Media/Publicity Wrangler E-mail Address' 321 'A/V Wrangler Name' => 'text',322 'A/V Wrangler E-mail Address' => 'text',323 'Party Wrangler Name' => 'text',324 'Party Wrangler E-mail Address' => 'text',325 'Travel Wrangler Name' => 'text',326 'Travel Wrangler E-mail Address' => 'text',327 'Safety Wrangler Name' => 'text',328 'Safety Wrangler E-mail Address' => 'text',329 'Mentor Name' => 'text',330 'Mentor E-mail Address' => 'text',363 'Organizer Name' => 'text', 364 'WordPress.org Username' => 'text', 365 'Email Address' => 'text', 366 'Telephone' => 'text', 367 'Mailing Address' => 'textarea', 368 'Sponsor Wrangler Name' => 'text', 369 'Sponsor Wrangler E-mail Address' => 'text', 370 'Budget Wrangler Name' => 'text', 371 'Budget Wrangler E-mail Address' => 'text', 372 'Venue Wrangler Name' => 'text', 373 'Venue Wrangler E-mail Address' => 'text', 374 'Speaker Wrangler Name' => 'text', 375 'Speaker Wrangler E-mail Address' => 'text', 376 'Food/Beverage Wrangler Name' => 'text', 377 'Food/Beverage Wrangler E-mail Address' => 'text', 378 'Swag Wrangler Name' => 'text', 379 'Swag Wrangler E-mail Address' => 'text', 380 'Volunteer Wrangler Name' => 'text', 381 'Volunteer Wrangler E-mail Address' => 'text', 382 'Printing Wrangler Name' => 'text', 383 'Printing Wrangler E-mail Address' => 'text', 384 'Design Wrangler Name' => 'text', 385 'Design Wrangler E-mail Address' => 'text', 386 'Website Wrangler Name' => 'text', 387 'Website Wrangler E-mail Address' => 'text', 388 'Social Media/Publicity Wrangler Name' => 'text', 389 'Social Media/Publicity Wrangler E-mail Address' => 'text', 390 'A/V Wrangler Name' => 'text', 391 'A/V Wrangler E-mail Address' => 'text', 392 'Party Wrangler Name' => 'text', 393 'Party Wrangler E-mail Address' => 'text', 394 'Travel Wrangler Name' => 'text', 395 'Travel Wrangler E-mail Address' => 'text', 396 'Safety Wrangler Name' => 'text', 397 'Safety Wrangler E-mail Address' => 'text', 398 'Mentor Name' => 'text', 399 'Mentor E-mail Address' => 'text', 331 400 332 401 'Venue Name' => 'text', … … 356 425 function admin_print_scripts() { 357 426 if ( get_post_type() == WCPT_POST_TYPE_ID ) : 427 358 428 ?> 359 429 … … 403 473 if ( !wcpt_has_access() ) 404 474 return false; 405 406 475 } 407 476 … … 414 483 */ 415 484 function user_profile_wordcamp( $profileuser ) { 416 417 485 if ( !wcpt_has_access() ) 418 486 return false; 419 420 487 ?> 421 488 422 489 <h3><?php _e( 'WordCamps', 'wcpt' ); ?></h3> 490 423 491 <table class="form-table"> 424 492 <tr valign="top"> 425 493 <th scope="row"><?php _e( 'WordCamps', 'wcpt' ); ?></th> 494 426 495 <td> 427 428 496 </td> 429 497 </tr> … … 455 523 456 524 /** 525 * Display the status of a WordCamp post 526 * 527 * @param array $states 528 * 529 * @return array 530 */ 531 public function display_post_states( $states ) { 532 global $post; 533 534 if ( $post->post_type != WCPT_POST_TYPE_ID ) { 535 return $states; 536 } 537 538 $status = get_post_status_object( $post->post_status ); 539 if ( get_query_var( 'post_status' ) != $post->post_status ) { 540 $states[ $status->name ] = $status->label; 541 } 542 543 return $states; 544 } 545 546 /** 457 547 * column_data ( $column, $post_id ) 458 548 * … … 472 562 473 563 case 'wcpt_date' : 474 475 564 // Has a start date 476 565 if ( $start = wcpt_get_wordcamp_start_date() ) { … … 526 615 echo implode( ' - ', (array) $wc ); 527 616 } 617 528 618 return $actions; 529 619 } … … 531 621 /** 532 622 * Trigger actions related to WordCamps being scheduled. 533 *534 * When an application is submitted, a `wordcamp` post is created with a `draft` status. When it's accepted535 * to the planning schedule the status changes to `pending`, and when it's accepted for the final schedule536 * the status changes to 'publish'.537 623 * 538 624 * @param string $new_status … … 545 631 } 546 632 633 if ( $new_status == $old_status ) { 634 return; 635 } 636 637 if ( $old_status == 'wcpt-pre-planning' && $new_status == 'wcpt-pre-planning' ) { 638 do_action( 'wcpt_added_to_planning_schedule', $post ); 639 } elseif ( $old_status == 'wcpt-needs-schedule' && $new_status == 'wcpt-scheduled' ) { 640 do_action( 'wcpt_added_to_final_schedule', $post ); 641 } 642 643 // back-compat for old statuses 547 644 if ( 'draft' == $old_status && 'pending' == $new_status ) { 548 645 do_action( 'wcpt_added_to_planning_schedule', $post ); … … 550 647 do_action( 'wcpt_added_to_final_schedule', $post ); 551 648 } 649 650 // todo add new triggers - which ones? 651 } 652 653 /** 654 * Log when the post status changes 655 * 656 * @param string $new_status 657 * @param string $old_status 658 * @param WP_Post $post 659 */ 660 public function log_status_changes( $new_status, $old_status, $post ) { 661 if ( $new_status === $old_status || $new_status == 'auto-draft' ) { 662 return; 663 } 664 665 if ( empty( $post->post_type ) || WCPT_POST_TYPE_ID != $post->post_type ) { 666 return; 667 } 668 669 $old_status = get_post_status_object( $old_status ); 670 $new_status = get_post_status_object( $new_status ); 671 672 add_post_meta( $post->ID, '_status_change', array( 673 'timestamp' => time(), 674 'user_id' => get_current_user_id(), 675 'message' => sprintf( '%s → %s', $old_status->label, $new_status->label ), 676 ) ); 552 677 } 553 678 … … 581 706 582 707 /** 583 * Force WordCamp posts to go through the expected status progression. 584 * 585 * They should start as drafts, then move to pending, and then be published. This is necessary because 586 * many automated processes (e.g., Organizer Reminder emails) are triggered when the post moves from 587 * one status to another, and deviations from the expected progression can cause bugs. 588 * 589 * Posts should still be allowed to move backwards in the progression, though. 708 * Enforce a valid post status for WordCamps. 590 709 * 591 710 * @param array $post_data … … 593 712 * @return array 594 713 */ 595 public function enforce_post_status_progression( $post_data, $post_data_raw ) { 596 if ( WCPT_POST_TYPE_ID == $post_data['post_type'] && ! empty( $_POST ) ) { 597 $previous_post_status = get_post( absint( $_POST['post_ID'] ) ); 598 $previous_post_status = $previous_post_status->post_status; 599 600 if ( 'pending' == $post_data['post_status'] && ! in_array( $previous_post_status, array( 'draft', 'pending', 'publish' ) ) ) { 601 $this->active_admin_notices[] = 2; 602 $post_data['post_status'] = $previous_post_status; 714 public function enforce_post_status( $post_data, $post_data_raw ) { 715 if ( $post_data['post_type'] != WCPT_POST_TYPE_ID || empty( $_POST['post_ID'] ) ) { 716 return $post_data; 717 } 718 719 $post = get_post( $_POST['post_ID'] ); 720 if ( ! $post ) { 721 return $post_data; 722 } 723 724 if ( ! empty( $post_data['post_status'] ) ) { 725 // Only network admins can change WordCamp statuses. 726 if ( ! current_user_can( 'network_admin' ) ) { 727 $post_data['post_status'] = $post->post_status; 603 728 } 604 729 605 if ( 'publish' == $post_data['post_status'] && ! in_array( $previous_post_status, array( 'pending', 'publish' ) ) ) { 606 $this->active_admin_notices[] = 2; 607 $post_data['post_status'] = $previous_post_status; 730 // Enforce a valid status. 731 $statuses = array_keys( WordCamp_Loader::get_post_statuses() ); 732 $statuses = array_merge( $statuses, array( 'trash' ) ); 733 734 if ( ! in_array( $post_data['post_status'], $statuses ) ) { 735 $post_data['post_status'] = $statuses[0]; 608 736 } 609 737 } … … 627 755 $min_site_id = apply_filters( 'wcpt_require_complete_meta_min_site_id', '2416297' ); 628 756 629 $required_p ending_fields = $this->get_required_fields( 'pending' );630 $required_ publish_fields = $this->get_required_fields( 'publish' );757 $required_pre_planning_fields = $this->get_required_fields( 'pre-planning' ); 758 $required_scheduled_fields = $this->get_required_fields( 'scheduled' ); 631 759 632 760 // Check pending posts 633 if ( ' pending' == $post_data['post_status'] && absint( $_POST['post_ID'] ) > $min_site_id ) {634 foreach( $required_p ending_fields as $field ) {761 if ( 'wcpt-approved-pre-pl' == $post_data['post_status'] && absint( $_POST['post_ID'] ) > $min_site_id ) { 762 foreach( $required_pre_planning_fields as $field ) { 635 763 $value = $_POST[ wcpt_key_to_str( $field, 'wcpt_' ) ]; 636 764 637 765 if ( empty( $value ) || 'null' == $value ) { 638 $post_data['post_status'] = 'draft'; 766 $post_data['post_status'] = 'wcpt-interview-sched'; 767 $this->active_admin_notices[] = 1; 768 break; 769 } 770 } 771 } 772 773 // Check published posts 774 if ( 'wcpt-scheduled' == $post_data['post_status'] && isset( $_POST['post_ID'] ) && absint( $_POST['post_ID'] ) > $min_site_id ) { 775 foreach( $required_scheduled_fields as $field ) { 776 $value = $_POST[ wcpt_key_to_str( $field, 'wcpt_' ) ]; 777 778 if ( empty( $value ) || 'null' == $value ) { 779 $post_data['post_status'] = 'wcpt-needs-schedule'; 639 780 $this->active_admin_notices[] = 3; 640 781 break; … … 643 784 } 644 785 645 // Check published posts646 if ( 'publish' == $post_data['post_status'] && isset( $_POST['post_ID'] ) && absint( $_POST['post_ID'] ) > $min_site_id ) {647 foreach( $required_publish_fields as $field ) {648 $value = $_POST[ wcpt_key_to_str( $field, 'wcpt_' ) ];649 650 if ( empty( $value ) || 'null' == $value ) {651 $post_data['post_status'] = 'pending';652 $this->active_admin_notices[] = 1;653 break;654 }655 }656 }657 658 786 return $post_data; 659 787 } … … 662 790 * Get a list of fields required to move to a certain post status 663 791 * 664 * @param string $status 'p ending' | 'publish' | 'any'792 * @param string $status 'pre-planning' | 'scheduled' | 'any' 665 793 * 666 794 * @return array 667 795 */ 668 796 public static function get_required_fields( $status ) { 669 $p ending = array( 'E-mail Address' );670 671 $ publish= array(797 $pre_planning = array( 'E-mail Address' ); 798 799 $scheduled = array( 672 800 // WordCamp 673 801 'Start Date (YYYY-mm-dd)', … … 691 819 692 820 switch ( $status ) { 693 case 'p ending':694 $required_fields = $p ending;821 case 'pre-planning': 822 $required_fields = $pre_planning; 695 823 break; 696 824 697 case ' publish':698 $required_fields = $ publish;825 case 'scheduled': 826 $required_fields = $scheduled; 699 827 break; 700 828 701 829 case 'any': 702 830 default: 703 $required_fields = array_merge( $p ending, $publish);831 $required_fields = array_merge( $pre_planning, $scheduled ); 704 832 break; 705 833 } … … 751 879 1 => array( 752 880 'type' => 'error', 753 'notice' => __( 'This WordCamp cannot be published until all of its required metadata is filled in.', 'wordcamporg' ), 754 ), 755 756 2 => array( 757 'type' => 'error', 758 'notice' => sprintf( 759 __( 760 'WordCamps must start as drafts, then be set as pending, and then be published. The post status has been reset to <strong>%s</strong>.', // todo improve language 761 'wordcamporg' 762 ), 763 $post->post_status 764 ) 881 'notice' => __( 'This WordCamp cannot be approved for pre-planning until all of its required metadata is filled in.', 'wordcamporg' ), 765 882 ), 766 883 767 884 3 => array( 768 885 'type' => 'error', 769 'notice' => __( 'This WordCamp cannot be set to pendinguntil all of its required metadata is filled in.', 'wordcamporg' ),886 'notice' => __( 'This WordCamp cannot be added to the schedule until all of its required metadata is filled in.', 'wordcamporg' ), 770 887 ), 771 888 ); … … 786 903 } 787 904 } 905 } 906 907 /** 908 * Render the WordCamp status meta box. 909 */ 910 public function metabox_status( $post ) { 911 require_once( WCPT_DIR . 'views/wordcamp/metabox-status.php' ); 912 } 913 914 /** 915 * Render the WordCamp status meta box. 916 */ 917 public function original_application_metabox( $post ) { 918 $application_data = get_post_meta( $post->ID, '_application_data', true ); 919 require_once( WCPT_DIR . 'views/wordcamp/metabox-original-application.php' ); 788 920 } 789 921 } … … 885 1017 <?php if ( ! empty( $messages[ $key ] ) ) : ?> 886 1018 <?php if ( 'textarea' == $value ) { echo '<br />'; } ?> 887 1019 888 1020 <span class="description"><?php echo esc_html( $messages[ $key ] ); ?></span> 889 1021 <?php endif; ?>
Note: See TracChangeset
for help on using the changeset viewer.