Inventory_Presser_Admin_Location_Meta
Source Source
File: includes/admin/class-admin-location-meta.php
class Inventory_Presser_Admin_Location_Meta { /** * Adds hooks. * * @return void */ public function add_hooks() { // location taxonomy admin actions. add_action( 'location_add_form_fields', array( $this, 'add_location_fields' ) ); add_action( 'location_edit_form_fields', array( $this, 'edit_location_field' ), 10, 1 ); add_action( 'created_location', array( $this, 'save_location_meta' ), 10, 1 ); add_action( 'edited_location', array( $this, 'save_location_meta' ), 10, 1 ); // Load JavaScript for timepickers, draggable and repeating fields. add_action( 'admin_enqueue_scripts', array( $this, 'scripts_and_styles' ) ); } /** * Outputs fields that are added to the add term page. * * @return void */ public function add_location_fields() { ?><div class="form-field term-group location-tax"> <div class="form-wrap form-field"> <label><?php esc_html_e( 'Street Address', 'inventory-presser' ); ?></label> <input type="text" name="address_street" /> </div> <div class="form-wrap form-field"> <label><?php esc_html_e( 'Street Address line two', 'inventory-presser' ); ?></label> <input type="text" name="address_street_line_two" /> </div> <div class="form-wrap form-field"> <label><?php esc_html_e( 'City', 'inventory-presser' ); ?></label> <input type="text" name="address_city" /> </div> <div class="form-wrap form-field"> <label><?php esc_html_e( 'State', 'inventory-presser' ); ?></label> <input type="text" name="address_state" /> </div> <div class="form-wrap form-field"> <label><?php esc_html_e( 'Zip', 'inventory-presser' ); ?></label> <input type="text" name="address_zip" /> </div> <div class="form-wrap form-field"> <label><?php esc_html_e( 'Phone Numbers', 'inventory-presser' ); ?></label> <div class="repeat-group"> <div class="repeat-container"></div> <div class="repeat-this"> <div class="repeat-form"> <input type="text" name="phone_description[]" placeholder="<?php esc_attr_e( 'Label', 'inventory-presser' ); ?>" /> <input type="text" name="phone_number[]" placeholder="<?php esc_attr_e( 'Number', 'inventory-presser' ); ?>" required /> </div> <div class="repeat-buttons"> <span class="dashicons dashicons-menu repeat-move" title="<?php esc_attr_e( 'Drag to reposition', 'inventory-presser' ); ?>"></span> <span class="dashicons dashicons-trash repeat-delete" title="<?php esc_attr_e( 'Delete this phone number', 'inventory-presser' ); ?>"></span> </div> </div> <button type="button" class="repeat-add button action"><?php esc_html_e( 'Add Phone Number', 'inventory-presser' ); ?></button> </div> </div> <div class="form-wrap form-field"> <label><?php esc_html_e( 'Hours', 'inventory-presser' ); ?></label> <div class="repeat-group"> <div class="repeat-container"></div> <div class="repeat-this"> <div class="repeat-form"> <input type="text" name="hours_title[]" placeholder="<?php esc_attr_e( 'Title', 'inventory-presser' ); ?>" /> <table class="wp-list-table widefat fixed striped hours"> <thead> <th class="day-col"></th> <th><?php esc_html_e( 'Open', 'inventory-presser' ); ?></th> <th class="to-col"></th> <th><?php esc_html_e( 'Close', 'inventory-presser' ); ?></th> <th><abbr title="<?php esc_attr_e( 'Appointment', 'inventory-presser' ); ?>"><?php esc_html_e( 'Appt', 'inventory-presser' ); ?></abbr> <?php echo esc_html_e( 'Only', 'inventory-presser' ); ?></th> </thead> <tbody> <?php foreach ( array_keys( INVP::weekdays() ) as $index => $day ) { ?> <tr> <th><?php echo esc_html( ucfirst( $day ) ); ?></th> <td><input name="hours[<?php echo esc_attr( $index ); ?>][open][]" class="timepick" type="text"></td> <th>to</th> <td><input name="hours[<?php echo esc_attr( $index ); ?>][close][]" class="timepick" type="text"></td> <td> <select name="hours[<?php echo esc_attr( $index ); ?>][appt][]"> <option value="0"><?php esc_html_e( 'No', 'inventory-presser' ); ?></option> <option value="1"><?php esc_html_e( 'Yes', 'inventory-presser' ); ?></option> </select> </td> </tr> <?php } ?> </tbody> </table> </div> <div class="repeat-buttons"> <span class="dashicons dashicons-menu repeat-move" title="<?php esc_attr_e( 'Drag to reposition', 'inventory-presser' ); ?>"></span> <span class="dashicons dashicons-trash repeat-delete" title="<?php esc_attr_e( 'Delete this set of hours', 'inventory-presser' ); ?>"></span> </div> </div> <p class="description"><?php esc_html_e( 'When saving multiple sets of hours for a single location, position the primary showroom hours first.', 'inventory-presser' ); ?></p> <p><button type="button" class="repeat-add button action"><?php esc_html_e( 'Add Hours Block', 'inventory-presser' ); ?></button></p> </div> </div> </div> <?php } /** * Outputs HTML that renders additional fields for the edit term screen. * * @param WP_Term $term Current taxonomy term object. * @return void */ public function edit_location_field( $term ) { $meta = get_term_meta( $term->term_id ); ?> <tr class="form-field term-group-wrap"> <th scope="row"> <label><?php esc_html_e( 'Street Address', 'inventory-presser' ); ?></label> </th> <td> <input type="text" name="address_street" value="<?php echo esc_attr( $meta['address_street'][0] ?? '' ); ?>" /> </td> </tr> <tr class="form-field term-group-wrap"> <th scope="row"> <label><?php esc_html_e( 'Street Address line two', 'inventory-presser' ); ?></label> </th> <td> <input type="text" name="address_street_line_two" value="<?php echo esc_attr( $meta['address_street_line_two'][0] ?? '' ); ?>" /> </td> </tr> <tr class="form-field term-group-wrap"> <th scope="row"> <label><?php esc_html_e( 'City', 'inventory-presser' ); ?></label> </th> <td> <input type="text" name="address_city" value="<?php echo esc_attr( $meta['address_city'][0] ?? '' ); ?>" /> </td> </tr> <tr class="form-field term-group-wrap"> <th scope="row"> <label><?php esc_html_e( 'State', 'inventory-presser' ); ?></label> </th> <td> <input type="text" name="address_state" value="<?php echo esc_attr( $meta['address_state'][0] ?? '' ); ?>" /> </td> </tr> <tr class="form-field term-group-wrap"> <th scope="row"> <label><?php esc_html_e( 'Zip', 'inventory-presser' ); ?></label> </th> <td> <input type="text" name="address_zip" value="<?php echo esc_attr( $meta['address_zip'][0] ?? '' ); ?>" /> </td> </tr> </tbody> </table> <h2 class="title"><?php esc_html_e( 'Phone Numbers & Hours', 'inventory-presser' ); ?></h2> <table class="form-table" role="presentation"> <tbody> <tr class="form-field term-group-wrap"> <th scope="row"><label><?php esc_html_e( 'Phone Numbers', 'inventory-presser' ); ?></label></th> <td> <div class="repeat-group"> <div class="repeat-container"> <?php $phones = INVP::get_phones( $term->term_id ); if ( ! empty( $phones ) ) { foreach ( $phones as $phone ) { ?> <div class="repeated"> <div class="repeat-form"> <?php printf( '<input type="hidden" name="phone_uid[]" value="%s" />' . '<input type="text" name="phone_description[]" value="%s" placeholder="%s" />' . '<input type="text" name="phone_number[]" value="%s" placeholder="%s" />', esc_attr( $phone['uid'] ), esc_attr( $phone['description'] ), esc_attr__( 'Label', 'inventory-presser' ), esc_attr( $phone['number'] ), esc_attr__( 'Number', 'inventory-presser' ) ); ?> </div> <div class="repeat-buttons"> <span class="dashicons dashicons-menu repeat-move"></span> <span class="dashicons dashicons-trash repeat-delete"></span> </div> </div> <?php } } ?> </div> <div class="repeat-this"> <div class="repeat-form"> <input type="text" name="phone_description[]" placeholder="<?php esc_attr_e( 'Label', 'inventory-presser' ); ?>" /> <input type="text" name="phone_number[]" placeholder="<?php esc_attr_e( 'Number', 'inventory-presser' ); ?>" /> </div> <div class="repeat-buttons"> <span class="dashicons dashicons-menu repeat-move" title="<?php esc_attr_e( 'Drag to reposition', 'inventory-presser' ); ?>"></span> <span class="dashicons dashicons-trash repeat-delete" title="<?php esc_attr_e( 'Delete this set of hours', 'inventory-presser' ); ?>"></span> </div> </div> <button type="button" class="repeat-add button action"><?php esc_html_e( 'Add Phone Number', 'inventory-presser' ); ?></button> </div> </td> </tr> <tr class="form-field term-group-wrap"> <th scope="row"><label><?php esc_html_e( 'Hours', 'inventory-presser' ); ?></label></th> <td> <div class="repeat-group"> <div class="repeat-container"> <?php $hours_sets = INVP::get_hours( $term->term_id ); $days = array_keys( INVP::weekdays() ); if ( ! empty( $hours_sets ) ) { foreach ( $hours_sets as $hours ) { ?> <div class="repeated"> <div class="repeat-form"> <input type="text" name="hours_title[]" placeholder="<?php esc_attr_e( 'Title', 'inventory-presser' ); ?>" value="<?php echo esc_attr( $hours['title'] ); ?>" /> <input type="hidden" name="hours_uid[]" placeholder="<?php esc_attr_e( 'Title', 'inventory-presser' ); ?>" value="<?php echo esc_attr( $hours['uid'] ); ?>" /> <table class="repeater-table hours"> <thead> <th class="day-col"></th> <th><?php esc_html_e( 'Open', 'inventory-presser' ); ?></th> <th class="to-col"></th> <th><?php esc_html_e( 'Close', 'inventory-presser' ); ?></th> <th><abbr title="<?php esc_attr_e( 'Appointment', 'inventory-presser' ); ?>"><?php esc_html_e( 'Appt', 'inventory-presser' ); ?></abbr> <?php echo esc_html_e( 'Only', 'inventory-presser' ); ?></th> </thead> <tbody> <?php for ( $d = 0; $d < 7; $d++ ) { ?> <tr> <td><?php echo esc_html( ucfirst( substr( $days[ $d ], 0, 3 ) ) ); ?></td> <td><input name="hours[<?php echo esc_attr( $d ); ?>][open][]" class="timepick" type="text" value="<?php echo esc_attr( $hours[ $days[ $d ] . '_open' ] ); ?>"></td> <td>to</td> <td><input name="hours[<?php echo esc_attr( $d ); ?>][close][]" class="timepick" type="text" value="<?php echo esc_attr( $hours[ $days[ $d ] . '_close' ] ); ?>"></td> <td> <select name="hours[<?php echo esc_attr( $d ); ?>][appt][]" autocomplete="off"> <option value="0"<?php echo ( '0' === $hours[ $days[ $d ] . '_appt' ] ) ? ' selected' : ''; ?>><?php esc_html_e( 'No', 'inventory-presser' ); ?></option> <option value="1"<?php echo ( '1' === $hours[ $days[ $d ] . '_appt' ] ) ? ' selected' : ''; ?>><?php esc_html_e( 'Yes', 'inventory-presser' ); ?></option> </select> </td> </tr> <?php } ?> </tbody> </table> </div> <div class="repeat-buttons"> <span class="dashicons dashicons-menu repeat-move" title="<?php esc_attr_e( 'Drag to reposition', 'inventory-presser' ); ?>"></span> <span class="dashicons dashicons-trash repeat-delete" title="<?php esc_attr_e( 'Delete this set of hours', 'inventory-presser' ); ?>"></span> </div> </div> <?php } } ?> </div> <div class="repeat-this"> <div class="repeat-form"> <input type="text" name="hours_title[]" placeholder="Title" /> <table class="repeater-table hours"> <thead> <th class="day-col"></th> <th><?php esc_html_e( 'Open', 'inventory-presser' ); ?></th> <th class="to-col"></th> <th><?php esc_html_e( 'Close', 'inventory-presser' ); ?></th> <th><abbr title="<?php esc_attr_e( 'Appointment', 'inventory-presser' ); ?>"><?php esc_html_e( 'Appt', 'inventory-presser' ); ?></abbr> <?php esc_html_e( 'Only', 'inventory-presser' ); ?></th> </thead> <tbody> <?php foreach ( array_keys( INVP::weekdays() ) as $d => $day ) { ?> <tr> <td><?php echo esc_html( ucfirst( substr( $days[ $d ], 0, 3 ) ) ); ?></td> <td><input name="hours[<?php echo esc_attr( $d ); ?>][open][]" class="timepick" type="text"></td> <td>to</td> <td><input name="hours[<?php echo esc_attr( $d ); ?>][close][]" class="timepick" type="text"></td> <td> <select name="hours[<?php echo esc_attr( $d ); ?>][appt][]"> <option value="0"><?php esc_html_e( 'No', 'inventory-presser' ); ?></option> <option value="1"><?php esc_html_e( 'Yes', 'inventory-presser' ); ?></option> </select> </td> </tr> <?php } ?> </tbody> </table> </div> <div class="repeat-buttons"> <span class="dashicons dashicons-menu repeat-move"></span> <span class="dashicons dashicons-trash repeat-delete"></span> </div> </div> <p class="description"><?php esc_html_e( 'When saving multiple sets of hours for a single location, position the primary showroom hours first.', 'inventory-presser' ); ?></p> <p><button type="button" class="repeat-add button action"><?php esc_html_e( 'Add Hours Set', 'inventory-presser' ); ?></button></p> </div> </td> </tr> <?php } /** * Creates a unique ID for a phone number or set of hours. * * @param string $salt_string Any string to be combined with rand() as the value to pass to md5(). * @return string A string of 12 characters. */ protected function generate_location_uid( $salt_string = null ) { return substr( md5( strval( wp_rand() ) . $salt_string ), 0, 12 ); } /** * Loads stylesheets and JavaScript files on pages that edit terms. * * @param string $hook The file name of the current page. * @return void */ public function scripts_and_styles( $hook ) { global $current_screen; if ( ( 'edit-tags.php' === $hook || 'term.php' === $hook ) && INVP::POST_TYPE === $current_screen->post_type && 'location' === $current_screen->taxonomy ) { $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; wp_enqueue_style( 'inventory-presser-timepicker-css', plugins_url( "/css/jquery.timepicker{$min}.css", INVP_PLUGIN_FILE_PATH ), array(), INVP_PLUGIN_VERSION ); wp_enqueue_style( 'invp-location-term', plugins_url( "/css/location-term{$min}.css", INVP_PLUGIN_FILE_PATH ), array(), INVP_PLUGIN_VERSION ); wp_enqueue_script( 'inventory-presser-timepicker' ); wp_enqueue_script( 'jquery-ui-sortable' ); wp_enqueue_script( 'inventory-presser-location' ); } } /** * When a location term is saved, put all of the values in the right term * meta fields. * * @param int $term_id Term ID. * @return void */ public function save_location_meta( $term_id ) { // Verify the nonce. What is the current hook name? switch ( current_action() ) { case 'created_location': if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce_add-tag'] ?? '' ) ), 'add-tag' ) ) { return; } break; case 'edited_location': if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ?? '' ) ), 'update-tag_' . $term_id ) ) { return; } break; } // Street address pieces. $keys = array( 'address_street', 'address_street_line_two', 'address_city', 'address_state', 'address_zip', ); foreach ( $keys as $key ) { if ( ! empty( $_POST[ $key ] ) ) { update_term_meta( $term_id, $key, sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) ); } } /** * Now that the street address has been updated, repopulate the term * description based on these pieces in term meta so the full street * address in the term description always matches the pieces of the * address. */ $this->update_location_term_description( $term_id ); // HOURS. if ( ! empty( $_POST['hours_title'] ) ) { $hours_count = count( $_POST['hours_title'] ) - 1; for ( $i = 0; $i <= $hours_count; $i++ ) { // if this is an update, carry the id through. $uid = ''; if ( isset( $_POST['hours_uid'][ $i ] ) ) { $uid = sanitize_text_field( wp_unslash( $_POST['hours_uid'][ $i ] ) ); } else { // generate a unique id for these hours. $uid = $this->generate_location_uid( $term_id . '_hours_' . $i ); } update_term_meta( $term_id, 'hours_' . strval( $i + 1 ) . '_uid', $uid ); // title of hours set. update_term_meta( $term_id, 'hours_' . strval( $i + 1 ) . '_title', sanitize_text_field( wp_unslash( $_POST['hours_title'][ $i ] ?? '' ) ) ); foreach ( array_keys( INVP::weekdays() ) as $d => $day ) { update_term_meta( $term_id, 'hours_' . strval( $i + 1 ) . '_' . $day . '_appt', sanitize_text_field( wp_unslash( $_POST['hours'][ $d ]['appt'][ $i ] ?? '' ) ) ); update_term_meta( $term_id, 'hours_' . strval( $i + 1 ) . '_' . $day . '_open', sanitize_text_field( wp_unslash( $_POST['hours'][ $d ]['open'][ $i ] ?? '' ) ) ); update_term_meta( $term_id, 'hours_' . strval( $i + 1 ) . '_' . $day . '_close', sanitize_text_field( wp_unslash( $_POST['hours'][ $d ]['close'][ $i ] ?? '' ) ) ); } } // delete hours in slots higher than we just filled or deletes are not possible. for ( $h = $hours_count + 1; $h < INVP::LOCATION_MAX_HOURS; $h++ ) { delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_uid' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_title' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_sunday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_sunday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_sunday_close' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_saturday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_saturday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_saturday_close' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_friday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_friday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_friday_close' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_thursday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_thursday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_thursday_close' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_wednesday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_wednesday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_wednesday_close' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_tuesday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_tuesday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_tuesday_close' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_monday_appt' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_monday_open' ); delete_term_meta( $term_id, 'hours_' . strval( $h ) . '_monday_close' ); } } // PHONE NUMBERS. if ( ! empty( $_POST['phone_number'] ) ) { $phones_count = 0; $phone_numbers = array_map( 'sanitize_text_field', wp_unslash( $_POST['phone_number'] ) ); foreach ( $phone_numbers as $i => $phone_number ) { $phone_number = sanitize_text_field( $phone_number ); if ( empty( $phone_number ) ) { continue; } update_term_meta( $term_id, 'phone_' . strval( $i + 1 ) . '_number', $phone_number ); update_term_meta( $term_id, 'phone_' . strval( $i + 1 ) . '_description', sanitize_text_field( wp_unslash( $_POST['phone_description'][ $i ] ?? '' ) ) ); // if this is an update, carry the id through. $uid = ''; if ( isset( $_POST['phone_uid'][ $i ] ) ) { $uid = sanitize_text_field( wp_unslash( $_POST['phone_uid'][ $i ] ) ); } else { // generate a unique id for this phone number. $uid = $this->generate_location_uid( $term_id . '_phone_' . $i ); } update_term_meta( $term_id, 'phone_' . strval( $i + 1 ) . '_uid', $uid ); ++$phones_count; } // delete phones in slots higher than we just filled or deletes are not possible. for ( $p = $phones_count + 1; $p < INVP::LOCATION_MAX_PHONES; $p++ ) { delete_term_meta( $term_id, 'phone_' . strval( $p ) . '_uid' ); delete_term_meta( $term_id, 'phone_' . strval( $p ) . '_description' ); delete_term_meta( $term_id, 'phone_' . strval( $p ) . '_number' ); } } } /** * Updates the term description with an address built from the term meta * values so the meta pieces and the description string match. * * @param int $term_id Term ID. * @return void */ protected function update_location_term_description( $term_id ) { $line_one = ''; $line_two = ''; $line_three = ''; $meta = get_term_meta( $term_id ); if ( ! empty( $meta['address_street'][0] ) ) { $line_one .= $meta['address_street'][0]; } if ( ! empty( $meta['address_street_line_two'][0] ) ) { $line_two .= $meta['address_street_line_two'][0]; } if ( ! empty( $meta['address_city'][0] ) ) { $line_three .= $meta['address_city'][0]; } if ( ! empty( $meta['address_state'][0] ) ) { $line_three .= ', ' . $meta['address_state'][0]; } if ( ! empty( $meta['address_zip'][0] ) ) { $line_three .= ' ' . $meta['address_zip'][0]; } $term = get_term( $term_id ); if ( empty( $term ) || is_wp_error( $term ) ) { return; } /** * This method is called from save_location_meta, which is hooked to * location_edited. Remove that hook or else an infinite loop happens. */ remove_action( 'edited_location', array( $this, 'save_location_meta' ), 10, 1 ); wp_update_term( $term->term_id, $term->taxonomy, array( 'description' => trim( $line_one . "\n" . trim( $line_two . "\n" . $line_three ) ), ) ); add_action( 'edited_location', array( $this, 'save_location_meta' ), 10, 1 ); } }
Expand full source codeCollapse full source codeView on Github
Methods Methods
- add_hooks — Adds hooks.
- add_location_fields — Outputs fields that are added to the add term page.
- edit_location_field — Outputs HTML that renders additional fields for the edit term screen.
- generate_location_uid — Creates a unique ID for a phone number or set of hours.
- save_location_meta — When a location term is saved, put all of the values in the right term meta fields.
- scripts_and_styles — Loads stylesheets and JavaScript files on pages that edit terms.
- update_location_term_description — Updates the term description with an address built from the term meta values so the meta pieces and the description string match.