-
Notifications
You must be signed in to change notification settings - Fork 58
feat: add support for displaying tags as labels #4381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jason10lee
wants to merge
23
commits into
trunk
Choose a base branch
from
feat/tag-labels
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+343
−0
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
1144256
feat: add support for displaying tags as labels
jason10lee ef2cd3f
fix: apply suggestions from Copilot code review
jason10lee 0fe60fb
fix: cleanup; Copilot suggestions
jason10lee 36e8403
fix: tweaks; Copilot suggestions
jason10lee 2b427be
chore: clean up comments
jason10lee 2521459
fix: incorporate feedback on names, outdated variables
jason10lee ae531b4
feat: rename settings; display flag_meta_key only if checkbox is checked
thomasguillot 75786a1
refactor: accept WP_Post object or post ID, as is done elsewhere
jason10lee 62cd9c7
feat: add support for setting tag labels on term creation
jason10lee c1978f9
refactor: upstream handles nonce verification; handling here breaks o…
jason10lee 5b53c1d
refactor: upstream handles nonce verification; handling here breaks o…
jason10lee 31e8baf
fix: reversed function parameters
jason10lee 77c21e3
refactor: renamed 'is_tag_label' to 'has_label'
jason10lee 53597d5
fix: apply Copilot suggestion
jason10lee b8b0e0f
refactor: centralized HTML generation function and interface to `news…
jason10lee f52c944
fix: add capabilities test
jason10lee 43e70ee
fix: populate `id` attributes to improve usability
jason10lee 3d829d5
fix: handle potential `WP_Error`, as suggested by Copilot
jason10lee 626bcc5
fix: do not clear `_np_label_flag` when labels disabled
jason10lee 849d7c8
refactor: drop dead isset() check on get_term_meta result
jason10lee cdcc843
fix: scope save_term to this UI's form submissions via nonce
jason10lee b7cc66f
style: wrap edit_term nonce in tr/td for valid HTML inside form-table
jason10lee de87b9e
fix: preserve _np_label_flag in edit form across enable/disable cycles
jason10lee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,318 @@ | ||
| <?php | ||
| /** | ||
| * Newspack Tag Labels | ||
| * | ||
| * @package Newspack | ||
| */ | ||
|
|
||
| namespace Newspack; | ||
|
|
||
| defined( 'ABSPATH' ) || exit; | ||
|
|
||
| /** | ||
| * Newspack Tag Labels | ||
| */ | ||
| class Tag_Labels { | ||
|
|
||
| /** | ||
| * Key names. | ||
| */ | ||
| const TAG_LABEL_META_KEY = '_np_label_enabled'; | ||
| const TAG_LABEL_FLAG_META_KEY = '_np_label_flag'; | ||
|
|
||
|
|
||
| // Helper functions for themes to get arrays of labels and flags. | ||
| /** | ||
| * Given a term, check if labels are enabled for it. | ||
| * | ||
| * @param WP_Term $term Term to check. | ||
| * | ||
| * @return bool | ||
| */ | ||
| public static function has_label( $term ) { | ||
| if ( ! $term || ! $term->term_id ) { | ||
| return false; | ||
| } | ||
| return ! empty( get_term_meta( $term->term_id, self::TAG_LABEL_META_KEY, true ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Given a term, return the flag (text) of its label and | ||
| * the link to the term archive. | ||
| * | ||
| * Will return null if label isn't enabled for the term. | ||
| * | ||
| * @param WP_Term $term Term to check. | ||
| * | ||
| * @return array|null As ['flag' => FLAG_NAME, 'link' => TERM_LINK]. | ||
| */ | ||
| public static function get_tag_label_for_term( $term ) { | ||
| if ( ! $term || ! $term->term_id || ! self::has_label( $term ) ) { | ||
| return null; | ||
| } | ||
|
|
||
| // A little fancy in case someone wants to give a tag a | ||
| // falsy label flag. Empty string still gets default value. | ||
| $term_label_flag = get_term_meta( $term->term_id, self::TAG_LABEL_FLAG_META_KEY, true ); | ||
| if ( '' === $term_label_flag ) { | ||
| $term_label_flag = $term->name; | ||
| } | ||
|
|
||
| $term_label_link = get_term_link( $term->term_id ); | ||
| if ( is_wp_error( $term_label_link ) ) { | ||
| return null; | ||
| } | ||
|
|
||
| return [ | ||
| 'flag' => $term_label_flag, | ||
| 'link' => $term_label_link, | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * Given a post ID, grab array of tag labels (if any) for it. | ||
| * | ||
| * @param int|WP_Post|null $post Post to check. | ||
| * | ||
| * @return array|null Elements as ['flag' => FLAG_NAME, 'link' => TERM_LINK]. | ||
| */ | ||
| public static function get_labels_for_post( $post ) { | ||
| if ( ! $post ) { | ||
| return null; | ||
| } | ||
|
|
||
| $post_id = ( is_a( $post, 'WP_Post' ) ? $post->ID : (int) $post ); | ||
| $post_terms = get_the_terms( $post_id, 'post_tag' ); | ||
|
|
||
| if ( empty( $post_terms ) || is_wp_error( $post_terms ) ) { | ||
| return []; | ||
| } | ||
|
|
||
| return array_filter( | ||
| array_map( | ||
| function( $term ) { | ||
| return self::get_tag_label_for_term( $term ); | ||
| }, | ||
| $post_terms | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Generates HTML for given tag labels. | ||
| * | ||
| * @param array $labels Labels to display. | ||
| * @param bool $links Whether to include links to tag archives. | ||
| * @param array $outer_classes Classes to apply to the outer container. | ||
| * @param array $inner_classes Classes to apply to the inner container. | ||
| * @param string $outer_element HTML element to use for the outer container. | ||
| * | ||
| * @return string Tag labels as HTML. | ||
| */ | ||
| public static function generate_html( $labels = null, $links = true, $outer_classes = array( 'tag-labels' ), $inner_classes = array( 'tag-label', 'flag' ), $outer_element = 'span' ) { | ||
|
jason10lee marked this conversation as resolved.
|
||
| if ( empty( $labels ) ) { | ||
| return ''; | ||
| } | ||
|
|
||
| $outer_element = in_array( $outer_element, [ 'span', 'div' ], true ) ? $outer_element : 'span'; | ||
|
|
||
| $labels_html = ''; | ||
| $labels_html .= '<' . $outer_element . ' class="' . join( ' ', array_map( 'esc_attr', $outer_classes ) ) . '">'; | ||
| foreach ( $labels as $label ) { | ||
| if ( $links && isset( $label['flag'] ) && $label['link'] ) { | ||
| $labels_html .= '<a class="' . join( ' ', array_map( 'esc_attr', $inner_classes ) ) . '" href="' . esc_url( $label['link'] ) . '" rel="tag">' . esc_html( $label['flag'] ) . '</a>'; | ||
| } elseif ( isset( $label['flag'] ) ) { | ||
| $labels_html .= '<span class="' . join( ' ', array_map( 'esc_attr', $inner_classes ) ) . '">' . esc_html( $label['flag'] ) . '</span>'; | ||
| } | ||
| } | ||
| $labels_html .= '</' . $outer_element . '><!-- .tag-labels -->'; | ||
|
|
||
| return $labels_html; | ||
| } | ||
|
|
||
| /** | ||
| * Outputs HTML for given tag labels. | ||
| * | ||
| * @param array $labels Labels to display. | ||
| * @param bool $links Whether to include links to tag archives. | ||
| * @param string $outer_element HTML element to use for the outer container. | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function display( $labels = null, $links = true, $outer_element = 'span' ) { | ||
| if ( empty( $labels ) ) { | ||
| return; | ||
| } | ||
|
|
||
| echo wp_kses_post( self::generate_html( $labels, $links, array( 'tag-labels', 'cat-links' ), array( 'tag-label', 'flag' ), $outer_element ) . ' ' ); | ||
| } | ||
|
|
||
| /** | ||
| * Initialize hooks. | ||
| */ | ||
| public static function init() { | ||
| add_action( 'post_tag_pre_add_form', array( __CLASS__, 'enqueue_scripts' ) ); | ||
| add_action( 'post_tag_term_edit_form_top', array( __CLASS__, 'enqueue_scripts' ) ); | ||
|
|
||
| add_action( 'post_tag_add_form_fields', [ __CLASS__, 'add_term' ] ); | ||
| add_action( 'post_tag_edit_form_fields', [ __CLASS__, 'edit_term' ] ); | ||
|
|
||
| add_action( 'created_post_tag', [ __CLASS__, 'save_term' ] ); | ||
| add_action( 'edited_post_tag', [ __CLASS__, 'save_term' ] ); | ||
| } | ||
|
|
||
| /** | ||
| * Enqueues js script | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function enqueue_scripts() { | ||
| wp_enqueue_script( | ||
| 'newspack_tag_labels', | ||
| Newspack::plugin_url() . '/dist/other-scripts/tag-labels.js', | ||
| [ 'jquery' ], | ||
| NEWSPACK_PLUGIN_VERSION, | ||
| true | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Term creation fields. | ||
| * | ||
| * Toggle to determine if the term is a label. | ||
| * Also, override for flag (text used on label). | ||
| */ | ||
| public static function add_term() { | ||
| $checkbox_id = self::TAG_LABEL_META_KEY; | ||
| ?> | ||
| <div class="form-field newspack-label-enable term-<?php echo esc_attr( self::TAG_LABEL_META_KEY ); ?>-wrap"> | ||
| <label for="<?php echo esc_attr( $checkbox_id ); ?>"><?php esc_html_e( 'Display as label', 'newspack-plugin' ); ?></label> | ||
| <input | ||
| aria-describedby="<?php echo esc_attr( self::TAG_LABEL_META_KEY ); ?>-description" | ||
| type="checkbox" | ||
| id="<?php echo esc_attr( $checkbox_id ); ?>" | ||
| name="<?php echo esc_attr( $checkbox_id ); ?>" | ||
| value="true" | ||
| > | ||
|
jason10lee marked this conversation as resolved.
|
||
| <p class="description" id="<?php echo esc_attr( self::TAG_LABEL_META_KEY ); ?>-description"> | ||
| <?php echo esc_html__( 'Show this tag as a highlighted label wherever posts are displayed.', 'newspack-plugin' ); ?> | ||
| </p> | ||
| </div> | ||
| <div class="form-field newspack-label-setting term-<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>-wrap" style="display: none;"> | ||
| <label for="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>"><?php esc_html_e( 'Label text', 'newspack-plugin' ); ?></label> | ||
| <input | ||
| aria-describedby="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>-description" | ||
| type="text" | ||
| id="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>" | ||
| name="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>" | ||
| placeholder="<?php echo esc_attr__( 'Enter custom label text', 'newspack-plugin' ); ?>" | ||
| value="" | ||
| disabled | ||
| > | ||
| <p class="description" id="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>-description"> | ||
| <?php echo esc_html__( 'Custom text to display instead of the tag name.', 'newspack-plugin' ); ?> | ||
| </p> | ||
| </div> | ||
| <?php wp_nonce_field( 'newspack_tag_labels_save', 'newspack_tag_labels_nonce' ); ?> | ||
| <?php | ||
| } | ||
|
|
||
| /** | ||
| * Term edit fields. | ||
| * | ||
| * Toggle to determine if the term is a label. | ||
| * Also, override for flag (text used on label). | ||
| * | ||
| * @param WP_Term $term The current WP_Term object. | ||
| */ | ||
| public static function edit_term( $term ) { | ||
| $checkbox_id = self::TAG_LABEL_META_KEY; | ||
| $is_label = self::has_label( $term ); | ||
|
|
||
| // Read the stored flag directly so the input value survives disable→re-enable cycles — | ||
| // get_tag_label_for_term() returns null when has_label() is false, which would hide it. | ||
| $stored_flag = get_term_meta( $term->term_id, self::TAG_LABEL_FLAG_META_KEY, true ); | ||
| $input_label_flag = ( '' === $stored_flag || $term->name === $stored_flag ) ? '' : $stored_flag; | ||
| ?> | ||
| <tr class="form-field newspack-label-enable term-<?php echo esc_attr( self::TAG_LABEL_META_KEY ); ?>-wrap"> | ||
| <th scope="row"><label for="<?php echo esc_attr( $checkbox_id ); ?>"><?php esc_html_e( 'Display as label', 'newspack-plugin' ); ?></label></th> | ||
| <td> | ||
| <input | ||
| aria-describedby="<?php echo esc_attr( self::TAG_LABEL_META_KEY ); ?>-description" | ||
| type="checkbox" | ||
| id="<?php echo esc_attr( $checkbox_id ); ?>" | ||
| name="<?php echo esc_attr( $checkbox_id ); ?>" | ||
| value="true" | ||
| <?php | ||
| checked( $is_label, true ); | ||
| ?> | ||
| > | ||
|
jason10lee marked this conversation as resolved.
jason10lee marked this conversation as resolved.
|
||
| <p class="description" id="<?php echo esc_attr( self::TAG_LABEL_META_KEY ); ?>-description"> | ||
| <?php echo esc_html__( 'Show this tag as a highlighted label wherever posts are displayed.', 'newspack-plugin' ); ?> | ||
| </p> | ||
| </td> | ||
| </tr> | ||
| <tr class="form-field newspack-label-setting term-<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>-wrap"<?php echo $is_label ? '' : ' style="display: none;"'; ?>> | ||
| <th scope="row"><label for="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>"><?php esc_html_e( 'Label text', 'newspack-plugin' ); ?></label></th> | ||
| <td> | ||
| <input | ||
| aria-describedby="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>-description" | ||
| type="text" | ||
| id="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>" | ||
| name="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>" | ||
| placeholder="<?php echo esc_attr( $term->name ); ?>" | ||
| value="<?php echo esc_attr( $input_label_flag ); ?>" | ||
| <?php | ||
| if ( ! $is_label ) { | ||
| echo ' disabled'; } | ||
|
jason10lee marked this conversation as resolved.
|
||
| ?> | ||
| > | ||
|
jason10lee marked this conversation as resolved.
jason10lee marked this conversation as resolved.
|
||
| <p class="description" id="<?php echo esc_attr( self::TAG_LABEL_FLAG_META_KEY ); ?>-description"> | ||
| <?php echo esc_html__( 'Custom text to display instead of the tag name.', 'newspack-plugin' ); ?> | ||
| </p> | ||
| </td> | ||
| </tr> | ||
| <tr><td colspan="2"><?php wp_nonce_field( 'newspack_tag_labels_save', 'newspack_tag_labels_nonce' ); ?></td></tr> | ||
| <?php | ||
| } | ||
|
|
||
| /** | ||
| * Store custom term meta on save. | ||
| * | ||
| * Bails on any term mutation that didn't originate from this UI's form — `created_post_tag` | ||
| * and `edited_post_tag` also fire on REST edits, importers, and third-party | ||
| * `wp_update_term()` calls, none of which post our fields. | ||
| * | ||
| * @param int $term_id Term ID. | ||
| */ | ||
| public static function save_term( $term_id ) { | ||
| if ( ! isset( $_POST['newspack_tag_labels_nonce'] ) | ||
| || ! wp_verify_nonce( | ||
| sanitize_text_field( wp_unslash( $_POST['newspack_tag_labels_nonce'] ) ), | ||
| 'newspack_tag_labels_save' | ||
| ) | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| if ( ! current_user_can( 'manage_categories' ) ) { | ||
| return; | ||
| } | ||
|
|
||
| // Save label data if label is enabled; otherwise kill it. | ||
| if ( ! empty( $_POST[ self::TAG_LABEL_META_KEY ] ) ) { | ||
| update_term_meta( $term_id, self::TAG_LABEL_META_KEY, true ); | ||
|
|
||
| // Save falsy values other than empty string in case someone wants a flag of '0' or something. | ||
| if ( isset( $_POST[ self::TAG_LABEL_FLAG_META_KEY ] ) && $_POST[ self::TAG_LABEL_FLAG_META_KEY ] !== '' ) { | ||
| update_term_meta( $term_id, self::TAG_LABEL_FLAG_META_KEY, sanitize_text_field( wp_unslash( $_POST[ self::TAG_LABEL_FLAG_META_KEY ] ) ) ); | ||
|
jason10lee marked this conversation as resolved.
|
||
| } else { | ||
| delete_term_meta( $term_id, self::TAG_LABEL_FLAG_META_KEY ); | ||
| } | ||
| } else { | ||
| delete_term_meta( $term_id, self::TAG_LABEL_META_KEY ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Tag_Labels::init(); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /* globals jQuery */ | ||
|
|
||
| ( function ( $ ) { | ||
| function toggleLabelSetting() { | ||
| const checkbox = $( '.newspack-label-enable input[type="checkbox"]' ); | ||
| const labelSettingRow = $( '.newspack-label-setting' ); | ||
|
|
||
| if ( checkbox.is( ':checked' ) ) { | ||
| labelSettingRow.show(); | ||
| labelSettingRow.find( 'input' ).prop( 'disabled', false ); | ||
| } else { | ||
| labelSettingRow.hide(); | ||
| labelSettingRow.find( 'input' ).prop( 'disabled', true ); | ||
| } | ||
| } | ||
|
|
||
| // Set initial state on page load. | ||
| toggleLabelSetting(); | ||
|
|
||
| // Update on checkbox change. | ||
| $( '.newspack-label-enable input[type="checkbox"]' ).on( 'change', toggleLabelSetting ); | ||
| } )( jQuery ); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.